:root{
  /* ---- motion ---------------------------------------------------------------------------
     Every transition on this site used the browser's default `ease`
     (cubic-bezier(.25,.1,.25,1)), which decelerates weakly and reads as mechanical - things
     arrive rather than settle. --ease is a strong ease-out: it leaves fast and lands slowly,
     which is what gives an interface the feeling of weight. --ease-soft is for colour and
     shadow, where a hard stop is not wanted.

     Durations are tokens so the whole site can be retimed in one place. Under ~120ms a change
     reads as a jump; over ~350ms it reads as sluggish. */
  --ease:cubic-bezier(.32,.72,0,1);
  --ease-soft:cubic-bezier(.4,0,.2,1);
  --dur-1:.14s;   /* colour, opacity - the eye barely tracks these */
  --dur-2:.22s;   /* the default: hover, focus, small movement */
  --dur-3:.38s;   /* something travelling a real distance */

  --bg:#0b1020; --bg-2:#0f1526; --surface:#151b2e; --surface-2:#1b2338;
  --border:#242d46; --border-2:#2f3a58;
  --text:#e7eaf3; --muted:#8b93a7; --faint:#5b6480;
  --accent:#6d5ef6; --accent-2:#22d3ee; --accent-grad:linear-gradient(135deg,#6d5ef6,#22d3ee);
  --success:#2dd4a7; --warn:#f5a524; --danger:#f0546a;
  --radius:14px; --radius-sm:10px;
  --shadow:0 10px 30px -12px rgba(0,0,0,.55);
  --font:'Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  --display:'Space Grotesk',var(--font);
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:radial-gradient(1200px 700px at 80% -10%,rgba(109,94,246,.14),transparent 60%),
             radial-gradient(900px 600px at -10% 10%,rgba(34,211,238,.10),transparent 55%),
             var(--bg);
  color:var(--text); font-family:var(--font); font-size:15px; line-height:1.55;
  min-height:100vh; -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4{font-family:var(--display);font-weight:600;letter-spacing:-.01em;margin:0 0 .5em}
.muted{color:var(--muted)}
.small{font-size:13px}

/* ---------- Buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:.5em;justify-content:center;
  padding:.65rem 1.1rem;border-radius:var(--radius-sm);border:1px solid var(--border-2);
  background:var(--surface-2);color:var(--text);font-weight:500;cursor:pointer;
  font-family:var(--font);font-size:14px;transition:.15s var(--ease);white-space:nowrap}
.btn:hover{border-color:var(--accent);transform:translateY(-1px)}
.btn-primary{background:var(--accent-grad);border:none;color:#fff;font-weight:600}
.btn-primary:hover{filter:brightness(1.07)}
.btn-danger{background:transparent;border-color:var(--danger);color:var(--danger)}
.btn-danger:hover{background:rgba(240,84,106,.12)}
.btn-sm{padding:.4rem .7rem;font-size:13px}
.btn-block{width:100%}

/* ---------- Forms ---------- */
label{display:block;margin:0 0 .35rem;font-size:13px;color:var(--muted);font-weight:500}
input,select,textarea{
  width:100%;padding:.7rem .85rem;background:var(--bg-2);border:1px solid var(--border);
  border-radius:var(--radius-sm);color:var(--text);font-family:var(--font);font-size:14px;outline:none;transition:.15s var(--ease)}
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(109,94,246,.15)}
.field{margin-bottom:1rem}
textarea{resize:vertical;min-height:90px}

/* ---------- Cards / panels ---------- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  padding:1.4rem;box-shadow:var(--shadow)}
.card + .card{margin-top:1.2rem}
.card-title{font-family:var(--display);font-size:18px;margin-bottom:1rem;display:flex;
  align-items:center;justify-content:space-between;gap:1rem}

/* ---------- Layout: sidebar dashboard ---------- */
.app{display:grid;grid-template-columns:250px 1fr;min-height:100vh}
.sidebar{background:linear-gradient(180deg,var(--bg-2),var(--bg));border-right:1px solid var(--border);
  padding:1.3rem 1rem;position:sticky;top:0;height:100vh;overflow-y:auto}
[dir=rtl] .sidebar{border-right:0;border-left:1px solid var(--border)}
.brand{display:flex;align-items:center;gap:.6rem;font-family:var(--display);font-weight:700;
  font-size:20px;padding:.3rem .5rem 1.2rem}
.brand .dot{width:14px;height:14px;border-radius:5px;background:var(--accent-grad);box-shadow:0 0 18px rgba(109,94,246,.7)}
.nav a{display:flex;align-items:center;gap:.7rem;padding:.7rem .8rem;border-radius:var(--radius-sm);
  color:var(--muted);font-weight:500;font-size:14px;margin-bottom:.15rem;transition:.15s var(--ease)}
.nav a:hover{background:var(--surface);color:var(--text)}
.nav a.active{background:var(--surface-2);color:var(--text)}
.nav a.active::before{content:"";position:absolute;left:0;width:3px;height:20px;background:var(--accent-grad);border-radius:2px;margin-left:-.8rem}
[dir=rtl] .nav a.active::before{left:auto;right:0;margin-left:0;margin-right:-.8rem}
.nav a{position:relative}
.nav-label{font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--faint);
  padding:1rem .8rem .4rem;font-weight:600}
.nav svg{width:18px;height:18px;flex:none;opacity:.8}
.main{padding:1.6rem 2rem 3rem;max-width:1200px;width:100%}
.topbar{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:1.6rem;flex-wrap:wrap}
.page-title{font-size:24px;margin:0}
.balance-chip{display:inline-flex;align-items:center;gap:.5rem;background:var(--surface);
  border:1px solid var(--border);border-radius:99px;padding:.5rem .95rem;font-weight:600}
.balance-chip .amt{background:var(--accent-grad);-webkit-background-clip:text;background-clip:text;color:transparent}

/* ---------- Stat grid ---------- */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:1rem;margin-bottom:1.4rem}
.stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.1rem 1.2rem;position:relative;overflow:hidden}
.stat::after{content:"";position:absolute;right:-20px;top:-20px;width:80px;height:80px;background:var(--accent-grad);opacity:.08;border-radius:50%}
.stat .k{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
.stat .v{font-family:var(--display);font-size:26px;font-weight:600;margin-top:.3rem}

/* ---------- Tables ---------- */
.table-wrap{overflow-x:auto;border-radius:var(--radius);border:1px solid var(--border)}
table{width:100%;border-collapse:collapse;font-size:14px;background:var(--surface)}
th,td{padding:.8rem 1rem;text-align:left;border-bottom:1px solid var(--border)}
th{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);font-weight:600;background:var(--bg-2)}
tr:last-child td{border-bottom:none}
tr:hover td{background:rgba(255,255,255,.02)}

/* ---------- Badges ---------- */
.badge{display:inline-flex;align-items:center;padding:.25rem .6rem;border-radius:99px;font-size:12px;font-weight:600}
.badge-pending{background:rgba(245,165,36,.14);color:var(--warn)}
.badge-progress{background:rgba(34,211,238,.14);color:var(--accent-2)}
.badge-completed{background:rgba(45,212,167,.14);color:var(--success)}
.badge-partial{background:rgba(109,94,246,.16);color:#a99bff}
.badge-canceled{background:rgba(240,84,106,.14);color:var(--danger)}

/* ---------- Alerts ---------- */
.alert{padding:.85rem 1.1rem;border-radius:var(--radius-sm);margin-bottom:1rem;font-size:14px;border:1px solid}
.alert-success{background:rgba(45,212,167,.1);border-color:rgba(45,212,167,.4);color:#8ff0d4}
.alert-error{background:rgba(240,84,106,.1);border-color:rgba(240,84,106,.4);color:#ffb4bf}
.alert-info{background:rgba(34,211,238,.1);border-color:rgba(34,211,238,.35);color:#a7ecf7}

/* ---------- Auth / landing ---------- */
.center-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem}
.auth-card{width:100%;max-width:410px}
.auth-card .brand{justify-content:center;font-size:24px}

.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
/* A grid ITEM defaults to `min-width:auto`, which is its MIN-CONTENT width - so one wide
   table inside a card stretched the whole track and pushed the page sideways. This is the
   same bug, and the same one-line fix, as `.main{min-width:0}`; the card's own
   `.table-wrap{overflow-x:auto}` already knew how to scroll and was simply never allowed to.
   Measured on /admin/report.php at 390px: 143px of horizontal page scroll -> 0. */
.grid-2 > *{min-width:0}
.row{display:flex;gap:1rem;flex-wrap:wrap;align-items:flex-end}
.flex{display:flex;gap:1rem;align-items:center;flex-wrap:wrap}  /* wrap: the blog top bar pushed the page to 561px at 360px wide */
.between{justify-content:space-between}
.mt{margin-top:1rem}.mb{margin-bottom:1rem}

/* ---------- Landing ---------- */
.hero{max-width:1100px;margin:0 auto;padding:5rem 2rem 3rem;text-align:center}
.hero .eyebrow{display:inline-block;font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--accent-2);border:1px solid var(--border-2);border-radius:99px;padding:.4rem 1rem;margin-bottom:1.5rem}
.hero h1{font-size:clamp(38px,6vw,68px);line-height:1.02;margin-bottom:1rem}
.hero h1 .grad{background:var(--accent-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p{font-size:18px;color:var(--muted);max-width:620px;margin:0 auto 2rem}
.hero-actions{display:flex;gap:.8rem;justify-content:center;flex-wrap:wrap}
.topnav{display:flex;align-items:center;justify-content:space-between;padding:1.2rem 2rem;max-width:1200px;margin:0 auto}
/* Phones: brand and the action buttons cannot share one row at 390px - they wrapped into
   three ragged lines with the buttons sitting on top of the brand name. Stack them:
   brand centered on its own row, actions centered underneath. Used by the public blog
   and catalogue headers. */
@media(max-width:560px){
  .topnav{flex-direction:column;align-items:stretch;gap:.6rem;padding:1rem}
  .topnav > .brand{justify-content:center}
  .topnav .flex{justify-content:center;gap:.5rem;row-gap:.5rem}
  .topnav .btn{padding:.4rem .7rem;font-size:.85rem}
  .topnav select{max-width:140px}
}
.features{max-width:1100px;margin:2rem auto;padding:0 2rem;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:1.2rem}
.feature{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.5rem}
.feature .ic{width:42px;height:42px;border-radius:12px;background:var(--surface-2);display:grid;place-items:center;margin-bottom:1rem}
.feature .ic svg{width:22px;height:22px;color:var(--accent-2)}

.mobile-toggle{display:none}
.backdrop{display:none}
@media(max-width:880px){
  .app{grid-template-columns:1fr}
  .sidebar{position:fixed;z-index:50;width:250px;left:-260px;transition:.25s var(--ease);box-shadow:var(--shadow)}
  .sidebar.open{left:0}
  /* RTL (fa/ar/ur): the drawer has to come from the right, otherwise it slides in
     over the content from the wrong edge on every Persian and Arabic phone. */
  [dir=rtl] .sidebar{left:auto;right:-260px}
  [dir=rtl] .sidebar.open{right:0;left:auto}
  .mobile-toggle{display:inline-flex}
  .main{padding:1.2rem}
  .grid-2{grid-template-columns:1fr}
  .backdrop{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:40}
  .backdrop.show{display:block}
}

/* ---------- Language switcher ---------- */
.lang-switch{display:flex;gap:.3rem;flex-wrap:wrap;padding:1rem .6rem .2rem}
.lang-switch a{font-size:18px;opacity:.5;filter:grayscale(.4);text-decoration:none;transition:.15s var(--ease)}
.lang-switch a:hover{opacity:.85}
.lang-switch a.on{opacity:1;filter:none;transform:scale(1.12)}

/* ============ Language dropdown ============ */
.lang-dd{position:relative}
.lang-btn{display:inline-flex;align-items:center;gap:.45rem;background:var(--surface);border:1px solid var(--border);
  color:var(--text);border-radius:99px;padding:.5rem .9rem;font-size:14px;font-weight:500;cursor:pointer;font-family:var(--font)}
.lang-btn:hover{border-color:var(--accent)}
.lang-btn .caret{opacity:.6;font-size:11px}
.lang-menu{position:absolute;top:calc(100% + .4rem);right:0;min-width:170px;background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow);padding:.35rem;display:none;z-index:60}
.lang-menu.open{display:block}
.lang-menu a{display:flex;align-items:center;gap:.5rem;padding:.55rem .7rem;border-radius:8px;color:var(--text);font-size:14px}
.lang-menu a:hover{background:var(--surface-2)}
.lang-menu a.on{color:var(--accent-2);font-weight:600}
[dir=rtl] .lang-menu{right:auto;left:0}

/* ============ Visual polish ============ */
.card{transition:border-color .18s var(--ease), transform .18s var(--ease)}
.card:hover{border-color:var(--border-2)}
.card-title{position:relative;padding-bottom:.9rem;border-bottom:1px solid var(--border)}
.stat{transition:transform .18s var(--ease), border-color .18s var(--ease)}
.stat:hover{transform:translateY(-2px);border-color:var(--accent)}
.stat .v{background:var(--accent-grad);-webkit-background-clip:text;background-clip:text;color:transparent;width:max-content}
.btn{border-radius:10px}
.btn-primary{box-shadow:0 6px 18px -8px var(--accent)}
.nav a{position:relative;overflow:hidden}
.nav a.active{box-shadow:inset 0 0 0 1px var(--border-2)}
.brand{font-size:21px;letter-spacing:-.02em}
.brand .dot{width:16px;height:16px;animation:pulse 3s ease-in-out infinite}
@keyframes pulse{0%,100%{box-shadow:0 0 14px rgba(109,94,246,.5)}50%{box-shadow:0 0 22px rgba(34,211,238,.7)}}
.table-wrap{border-radius:var(--radius)}
thead th{position:sticky;top:0}
tbody tr{transition:background .12s var(--ease)}
.badge{letter-spacing:.02em}
.page-title{background:linear-gradient(90deg,var(--text),var(--muted));-webkit-background-clip:text;background-clip:text;color:transparent}
.topbar{padding-bottom:1rem;border-bottom:1px solid var(--border);margin-bottom:1.4rem}
.sidebar{backdrop-filter:blur(6px)}
input,select,textarea{transition:border-color .15s var(--ease), box-shadow .15s var(--ease), background .15s var(--ease)}


/* ---------- Password show/hide field (moved out of the form body) ----------
   These lived in an inline <style> inside the login/signup <form>, which the W3C
   validator rejects and which re-downloads on every page view. asset_url() versions
   this file, so moving rules here still reaches visitors immediately. */
.pw-wrap{position:relative;display:block}
.pw-wrap input{width:100%;padding-inline-end:2.9rem}
.pw-eye{position:absolute;inset-inline-end:.55rem;top:50%;transform:translateY(-50%);
  background:none;border:0;padding:.25rem;cursor:pointer;line-height:1;font-size:1.05rem;
  opacity:.62;transition:opacity .15s var(--ease);border-radius:6px}
.pw-eye:hover{opacity:1}
.pw-eye:focus-visible{outline:2px solid var(--accent,#E9C46A);outline-offset:2px;opacity:1}

/* ---------- Services catalogue accordion (moved out of the page body) ---------- */
.svc-cta-cell{text-align:right;padding:0 12px;white-space:nowrap;width:1%}
.svc-cta{position:relative;display:inline-flex;align-items:center;gap:6px;
  transition:transform .2s var(--ease), box-shadow .2s var(--ease);
  background:linear-gradient(135deg,#E9C46A,#F4D58D);color:#1a1206;font-weight:700;font-size:.8rem;
  padding:7px 14px;border-radius:100px;box-shadow:0 6px 16px -8px rgba(233,196,106,.7);
  text-decoration:none;overflow:hidden}
.svc-cta .arw{transition:transform .25s var(--ease)}
.svc-cta:hover{transform:translateY(-2px) scale(1.03);box-shadow:0 12px 26px -8px rgba(233,196,106,.85)}
.svc-cta:hover .arw{transform:translateX(3px)}
.svc-row{transition:background .2s var(--ease)}
.svc-row:hover{background:rgba(233,196,106,.05)}
.svc-tag{display:inline-block;font-size:.68rem;font-weight:700;padding:2px 7px;border-radius:100px;margin-left:6px;vertical-align:middle;white-space:nowrap}
.tag-refill{background:rgba(45,212,167,.14);color:#2dd4a7;border:1px solid rgba(45,212,167,.35)}
.tag-cancel{background:rgba(155,140,255,.14);color:#9b8cff;border:1px solid rgba(155,140,255,.35)}
.svc-share{background:none;border:1px solid var(--border);color:var(--muted);width:34px;height:34px;border-radius:9px;cursor:pointer;font-size:.95rem;transition:.15s var(--ease);vertical-align:middle}
.svc-share:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px)}
.svc-share.copied{border-color:#2dd4a7;color:#2dd4a7}
/* highlight when deep-linked */
@keyframes svcpulse{0%{box-shadow:0 0 0 0 rgba(233,196,106,.6)}70%{box-shadow:0 0 0 10px rgba(233,196,106,0)}100%{box-shadow:0 0 0 0 rgba(233,196,106,0)}}
.svc-row.svc-highlight{background:rgba(233,196,106,.12)!important;outline:2px solid var(--accent);outline-offset:-2px;border-radius:8px;animation:svcpulse 1.4s ease 2}
.acc-search{margin-bottom:1rem}
.acc-search input{width:100%;padding:.7rem 1rem;border-radius:12px}
.acc{border:1px solid var(--border);border-radius:14px;margin-bottom:.7rem;overflow:hidden;background:var(--surface)}
.acc-head{width:100%;display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:16px 20px;background:none;border:none;cursor:pointer;color:var(--text);font-family:inherit;text-align:left;transition:background .2s var(--ease)}
.acc-head:hover{background:rgba(233,196,106,.05)}
.acc-name{font-family:var(--display),'Space Grotesk';font-weight:700;font-size:1.05rem}
.acc-meta{color:var(--muted);font-size:.85rem;display:flex;align-items:center;gap:.5rem}
.acc-arrow{transition:transform .25s var(--ease);display:inline-block}
.acc.open .acc-arrow{transform:rotate(180deg)}
.acc.open .acc-head{border-bottom:1px solid var(--border);background:rgba(233,196,106,.04)}
.acc-body{display:none}
/* One open category can hold 225 service rows. content-visibility lets the browser
   skip laying out the part that is scrolled off screen, which keeps opening a large
   category smooth on phones. contain-intrinsic-size stops the scrollbar jumping while
   that happens. Purely progressive: browsers without support render as before. */
.acc.open .acc-body{display:block;content-visibility:auto;contain-intrinsic-size:auto 600px}
@media(prefers-reduced-motion:reduce){.svc-cta,.acc-arrow{transition:none}}


/* ---------- PCB background layering (moved out of the page body for HTML validity) ---------- */
.app,.main,.center-wrap,.topnav,header,section,footer{position:relative;z-index:1}


/* ---------- Social sign-in buttons (moved out of the page body for HTML validity) ---------- */
.soc-btn{display:flex;align-items:center;justify-content:center;gap:8px;flex:1;padding:11px 14px;border-radius:11px;
  font-family:inherit;font-weight:600;font-size:.9rem;cursor:pointer;transition:.18s var(--ease);line-height:1;white-space:nowrap;
  background:var(--surface,rgba(255,255,255,.03));color:var(--text);border:1px solid var(--border)}
.soc-btn:hover{transform:translateY(-1px);background:var(--surface-2,rgba(255,255,255,.06))}
.soc-g:hover{border-color:rgba(255,255,255,.28);box-shadow:0 6px 18px -10px rgba(255,255,255,.3)}
.soc-tg{color:#eaf6ff}
.soc-tg:hover{border-color:#2AABEE;box-shadow:0 8px 20px -10px rgba(34,171,238,.6)}
.soc-row{display:flex;flex-direction:row;gap:10px}
.soc-btn svg{flex-shrink:0}
@media(max-width:360px){.soc-row{flex-direction:column}}
#socErr{display:none;margin-top:12px}
#socErr .btn{margin-top:8px}

/* Collapsible confirmation panel (the child-panel delete on /child_panels). Hidden until
   its button toggles .open — a <style> in the page body would break W3C validation. */
.confirm-box{display:none}
.confirm-box.open{display:block}

/* ---------- Claim bonus (/bonus) ----------
   Every colour here comes from a theme variable, so the page follows whichever theme the
   site (or a child panel) runs. The states live on [data-ui] on the card, set by
   assets/bonus.js from what the SERVER reported - the CSS never decides anything. */
.bonus-hero,.bonus-alert,.bonus-card{max-width:780px;margin-inline:auto}
.bonus-hero{position:relative;overflow:hidden;text-align:center;padding:1.8rem 1.2rem 1.4rem}
.bonus-hero::before{content:"";position:absolute;left:50%;top:-190px;width:440px;height:440px;
  transform:translateX(-50%);border-radius:50%;background:var(--accent);opacity:.10;
  filter:blur(70px);pointer-events:none}
.bonus-hero > *{position:relative}

.bonus-ring{position:relative;width:min(250px,68vw);margin:0 auto;aspect-ratio:1/1}
.bonus-ring svg{width:100%;height:100%;transform:rotate(-90deg);overflow:visible}
.bonus-ring circle{fill:none;stroke-width:9;stroke-linecap:round}
.bonus-track{stroke:var(--border-2);opacity:.6}
.bonus-arc{stroke:var(--accent);transition:stroke-dashoffset .5s linear, stroke .25s var(--ease), opacity .25s var(--ease)}
.bonus-ring-in{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:.35rem;padding:0 1rem}
.bonus-clock{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;
  font-size:clamp(2rem,8.5vw,2.75rem);font-weight:700;line-height:1;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums}
/* h:mm:ss is half again as wide as m:ss - shrink it so a cooldown never crowds the ring. */
.bonus-clock.is-long{font-size:clamp(1.5rem,6.2vw,2rem)}
.bonus-phase{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
  min-height:1.2em;text-align:center;line-height:1.2}

.bonus-msg{margin-top:1rem;min-height:2.6em;display:flex;align-items:center;justify-content:center;
  gap:.5rem;font-size:14px;color:var(--muted)}
.bonus-dot{width:8px;height:8px;border-radius:50%;background:var(--success);flex:none;
  animation:bonusPulse 1.8s ease-in-out infinite}
@keyframes bonusPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.7)}}

.bonus-actions{display:flex;gap:.6rem;justify-content:center;flex-wrap:wrap;margin-top:.4rem}
.bonus-actions .btn{min-width:12rem}
/* .btn sets display:inline-flex, which beats the UA [hidden] rule - so say it explicitly. */
.bonus-actions .btn[hidden]{display:none}
[data-ui="ready"] .bonus-arc{stroke:var(--success)}
[data-ui="ready"] .bonus-clock{color:var(--success)}
[data-ui="ready"] #bonusClaim{animation:bonusGlow 1.9s ease-in-out infinite}
@keyframes bonusGlow{0%,100%{box-shadow:0 6px 18px -10px var(--accent)}50%{box-shadow:0 10px 30px -6px var(--accent)}}
[data-ui="paused"] .bonus-arc{stroke:var(--warn)}
[data-ui="paused"] .bonus-clock{color:var(--warn)}
[data-ui="cooldown"] .bonus-arc{stroke:var(--success);opacity:.45}
[data-ui="blocked"] .bonus-arc{stroke:var(--danger)}
[data-ui="blocked"] .bonus-clock{color:var(--danger)}

.bonus-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:.6rem;margin-top:1.5rem}
.bonus-stat{background:var(--bg-2);border:1px solid var(--border);border-radius:var(--radius-sm);
  padding:.7rem .5rem;text-align:center}
.bonus-stat b{display:block;font-family:var(--display);font-size:1rem;font-weight:700;
  line-height:1.3;word-break:break-word}
.bonus-stat span{display:block;font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);margin-top:.15rem}
@media(max-width:560px){.bonus-stats{grid-template-columns:repeat(2,1fr)}}

.bonus-note{display:flex;gap:.6rem;align-items:flex-start;margin:1.4rem auto 0;max-width:36rem;
  font-size:13px;line-height:1.6;color:var(--muted);text-align:start;
  background:var(--bg-2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:.8rem .95rem}
.bonus-note i{font-style:normal;flex:none;color:var(--accent)}

@media (prefers-reduced-motion:reduce){
  body.wlc-folding .auth-card .card{animation:none;opacity:0}
  body.wlc-folding .auth-card .card::after{animation:none;opacity:0}
  .bonus-arc{transition:none}
  .bonus-dot,[data-ui="ready"] #bonusClaim{animation:none}
}

/* The walkthrough video on /bonus. max-width matches .bonus-hero so the two line up. */
.bonus-video{display:block;width:100%;border-radius:var(--radius-sm);background:#000}

/* ---------- Gift codes (/gift) ---------- */
.gift-card{max-width:520px;margin-inline:auto}
.gift-input{width:100%;text-align:center;letter-spacing:.22em;text-transform:uppercase;
  font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;
  font-size:1.25rem;font-weight:700;padding:.85rem .6rem}
.gift-input::placeholder{letter-spacing:.12em;font-weight:400;text-transform:none}
.gift-alert{max-width:520px;margin-inline:auto}
/* Label kept for screen readers rather than removed - the placeholder is not a label. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ---- admin tab bar (Services page) -------------------------------------------------
   The services admin was five cards stacked on one page; these group them. Colours come
   from the theme variables so a child panel's theme carries through unchanged. */
.tabbar{display:flex;gap:.35rem;flex-wrap:wrap;margin:0 0 1.1rem;border-bottom:1px solid var(--border);padding-bottom:.6rem}
.tabbar .tab{display:inline-block;padding:.5rem .95rem;border-radius:10px 10px 0 0;
  color:var(--muted);text-decoration:none;font-weight:600;font-size:.92rem;
  border:1px solid transparent;border-bottom:none;transition:color .15s var(--ease), background .15s var(--ease)}
.tabbar .tab:hover{color:var(--text);background:rgba(255,255,255,.04)}
.tabbar .tab.is-on{color:var(--accent);background:var(--card);border-color:var(--border);
  box-shadow:inset 0 -2px 0 var(--accent)}
@media (max-width:600px){.tabbar .tab{padding:.45rem .7rem;font-size:.86rem}}

/* ---- compact row actions (admin services list) -------------------------------------
   Six equal buttons per row read as a wall of choices. The two primary ones stay
   buttons; the rest become quiet text actions on a second line. */
.row-acts{display:flex;flex-wrap:wrap;gap:.3rem;align-items:center}
.row-acts + .row-acts{margin-top:.35rem;gap:.65rem}
.row-acts form{margin:0}
.linkish{background:none;border:0;padding:0;cursor:pointer;color:var(--muted);
  font-size:.82rem;font-family:inherit;text-decoration:underline;text-underline-offset:2px}
.linkish:hover{color:var(--text)}
.linkish.is-off{color:var(--warn)}
.linkish.is-danger{color:var(--danger);opacity:.75}
.linkish.is-danger:hover{opacity:1}

/* ---- admin: narrow screens ---------------------------------------------------------
   `.main` is a grid item, and a grid item's default `min-width:auto` is its MIN-CONTENT
   width - so one wide row anywhere inside stretched the whole track and the page scrolled
   sideways. Measured on the admin services page at 390px before this: 436px of horizontal
   overflow on the list, 83px on routing, 142px on categories. `min-width:0` lets the track
   shrink and everything inside (the tab bar, .grid-2, .table-wrap) then does what it was
   already written to do. */
.main{min-width:0}
.table-wrap{max-width:100%}
@media(max-width:880px){
  /* a category row is a flex line of a rename box plus five buttons; it cannot fit a phone
     on one line, and not wrapping is what made it 447px wide */
  .cat-row{flex-wrap:wrap}
  .cat-row form[style*="flex:1"]{flex:1 1 100%}
  /* last resort for the tab bar: it wraps, but if a future tab is long enough not to,
     scroll it rather than the page */
  .tabbar{flex-wrap:wrap;overflow-x:auto;-webkit-overflow-scrolling:touch}
}
/* the "already using more than one provider" shortcuts carry a service name, so they are as
   long as the name is. Let them wrap instead of pushing the page sideways on a phone. */
.route-chip{max-width:100%;white-space:normal;text-align:start;overflow-wrap:anywhere;line-height:1.35}
/* Inline <code> carries JSON samples on /api - `[{"service":1,"name":"Followers",...}]` has no
   spaces in it, so it is one unbreakable 744px token and it pushed that page 396px wide on a
   phone. Break inside the token; a sample response is for reading, not for selecting whole. */
code{overflow-wrap:anywhere;word-break:break-word}

/* ---- admin tables on a phone -------------------------------------------------------
   A seven-column table inside `.table-wrap{overflow-x:auto}` does not break the page, and
   that is what every overflow measurement said. It is still unusable on a phone: measured
   at 390px, 442 of the services list's controls - every Rate box, every Edit and Routing
   button - sat outside the viewport, reachable only by scrolling the table sideways to a
   column whose header had scrolled away.

   Below 760px each row becomes a block and each cell prints its own column name from
   `data-label`, so a row reads top to bottom and its actions are simply the last line.
   The cells with no header (the action column) pass an empty label and get no prefix. */
@media(max-width:760px){
  .stack-table, .stack-table tbody, .stack-table tr, .stack-table td{display:block;width:100%}
  .stack-table thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  .stack-table tr{border:1px solid var(--border);border-radius:var(--radius-sm);
    background:var(--surface);margin-bottom:.7rem;padding:.35rem .1rem}
  .stack-table tr:hover td{background:transparent}
  .stack-table td{border-bottom:1px solid var(--border);padding:.5rem .8rem;
    display:grid;grid-template-columns:minmax(6.5rem,auto) 1fr;gap:.5rem;align-items:start}
  .stack-table td:last-child{border-bottom:none}
  .stack-table td::before{content:attr(data-label);color:var(--muted);font-size:11px;
    text-transform:uppercase;letter-spacing:.06em;font-weight:600;padding-top:.15rem}
  /* the action cell has no column name, so it spans the full width instead of leaving a gap */
  .stack-table td[data-label=""]{grid-template-columns:1fr}
  .stack-table td[data-label=""]::before{display:none}
  /* a name that wrapped to six lines inside a 90px column now has the whole width */
  .stack-table td[style*="max-width"]{max-width:none!important}
  .stack-table td[style*="white-space:nowrap"]{white-space:normal!important}
}
/* A card title is a flex row of a heading plus whatever buttons that card offers, and it never
   wrapped - four action buttons on the auto-blog card gave it a min-content width of 982px and
   pushed /admin/blog.php 636px sideways at 390px. Wrapping costs nothing on a wide screen,
   where the row fits anyway. */
.card-title{flex-wrap:wrap;row-gap:.5rem}
/* The "other categories" links on a /services/<category> page carry a whole category name, and
   `.btn` is white-space:nowrap - correct for a button, wrong for one whose label is
   "Buy Telegram Auto Views - Future Posts [Country Targeted]". Measured 423px wide inside a
   390px viewport, which is where that page's 58px of horizontal scroll came from. */
.cat-link{white-space:normal;text-align:center;max-width:100%;overflow-wrap:anywhere;line-height:1.35}

/* =====================================================================================
   Page CSS that used to sit in a <style> element inside <body>.

   Appended at the END on purpose: in the body these came after app.css in document order
   and so won the cascade on equal specificity. Keeping them last preserves that exactly.
   Everything below is carried over VERBATIM - see the note in the migration script about
   what re-serialising it did to the @media blocks.
   ===================================================================================== */

/* ---- the customer dashboard (/dashboard) — was inline in dashboard.php ---- */
.dash-head{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;margin-bottom:1.4rem;flex-wrap:wrap}
.dash-title{font-size:1.5rem;margin:0 0 .2rem}
.dash-sub{margin:0;font-size:.95rem}
.stat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin-bottom:1rem}
.stat-card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:14px 16px;display:flex;align-items:center;gap:14px;transition:.2s var(--ease)}
.stat-card:hover{border-color:var(--border-2,var(--border));transform:translateY(-2px)}
.stat-ic{width:40px;height:40px;border-radius:11px;display:flex;align-items:center;justify-content:center;font-size:1.2rem;flex:none}
.stat-meta{flex:1;min-width:0}
.stat-k{color:var(--muted);font-size:.8rem;margin-bottom:.1rem}
.stat-v{font-family:var(--display);font-weight:700;font-size:1.35rem;letter-spacing:-.02em;line-height:1.1}
.stat-cta{font-size:.78rem;color:var(--accent);font-weight:600;text-decoration:none;white-space:nowrap;flex:none;align-self:center}
.stat-cta:hover{text-decoration:underline}
.dash-grid{display:grid;grid-template-columns:1.5fr 1fr;gap:1rem;margin-bottom:1rem}
.chart-card{display:flex;flex-direction:column}
.chart-legend{display:inline-flex;flex-wrap:wrap;gap:14px;font-size:.8rem;font-weight:500;color:var(--muted)}
.chart-legend .lg-item{display:inline-flex;align-items:center;gap:6px}
.chart-legend .lg-dot{width:9px;height:9px;border-radius:50%;flex:none}
.chart-box{position:relative;height:320px;margin-top:.6rem}
#chartTT{position:absolute;pointer-events:none;opacity:0;transition:opacity .15s var(--ease);background:rgba(10,12,16,.96);border:1px solid var(--border);
  border-radius:12px;padding:12px 14px;min-width:200px;box-shadow:0 20px 50px -20px rgba(0,0,0,.8);backdrop-filter:blur(8px);z-index:10;font-size:.86rem}
#chartTT .tt-day{font-weight:700;margin-bottom:8px;font-size:.95rem;display:flex;justify-content:space-between;gap:10px}
#chartTT .tt-day .tot{color:var(--accent)}
#chartTT .tt-row{display:flex;align-items:center;gap:8px;padding:4px 0}
#chartTT .tt-row .dot{width:9px;height:9px;border-radius:3px;flex:none}
#chartTT .tt-row .lab{color:var(--muted)}
#chartTT .tt-row .bar{width:44px;height:5px;border-radius:3px;background:rgba(255,255,255,.08);overflow:hidden;flex:none}
#chartTT .tt-row .bar>i{display:block;height:100%;border-radius:3px}
#chartTT .tt-row .val{margin-left:auto;font-weight:600;white-space:nowrap}
.pie-wrap{display:flex;align-items:center;gap:1.2rem;margin-top:.6rem;flex-wrap:wrap}
.pie-box{position:relative;width:180px;height:180px;flex:none;margin:0 auto}
.pie-legend{list-style:none;padding:0;margin:0;flex:1;min-width:140px;display:flex;flex-direction:column;gap:.7rem}
.pie-legend li{display:flex;align-items:center;gap:.6rem;font-size:14px}
.pie-legend .sw{width:12px;height:12px;border-radius:4px;flex:none}
.pie-legend .n{margin-left:auto;font-weight:600;font-family:var(--display)}
.live-dot{display:inline-block;width:9px;height:9px;border-radius:50%;background:var(--success);margin-left:.4rem;animation:livepulse 1.6s infinite}
@keyframes livepulse{0%{box-shadow:0 0 0 0 rgba(45,212,167,.5)}70%{box-shadow:0 0 0 8px rgba(45,212,167,0)}100%{box-shadow:0 0 0 0 rgba(45,212,167,0)}}
@media(max-width:1000px){.stat-grid{grid-template-columns:1fr}}
@media(max-width:880px){.dash-grid{grid-template-columns:1fr}}
[dir=rtl] .pie-legend .n{margin-left:0;margin-right:auto}

/* ---- the new-order form (/order) — was inline in order.php ---- */
.order-layout{display:grid;grid-template-columns:640px 1fr;gap:1.2rem;align-items:start}
.order-card{max-width:640px;margin:0}
.order-desc{position:sticky;top:1rem}
.svc-desc-body{color:var(--muted);font-size:.92rem;line-height:1.7;white-space:pre-wrap;word-break:break-word}
@media(max-width:1050px){.order-layout{grid-template-columns:1fr}.order-desc{position:static}}

/* ---- the customer order list (/orders) — was inline in orders.php ---- */
.ord-prog{height:6px;border-radius:4px;background:rgba(255,255,255,.08);overflow:hidden;margin-bottom:3px}
.ord-prog-bar{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--accent),var(--accent-2,var(--accent)))}

/* ---- the deposit page (/addfunds) — was inline in addfunds.php ---- */
.pm-list{display:flex;flex-direction:column;gap:.5rem}
.pm-opt{display:flex;align-items:center;gap:.6rem;padding:.7rem .9rem;border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;background:var(--bg-2)}
.pm-opt input{width:auto}
.pm-opt .pm-min{margin-left:auto}
.history-scroll{max-height:360px;overflow-y:auto}
.history-scroll thead th{position:sticky;top:0;background:var(--surface);z-index:1}
.pm-grid{align-items:stretch}
.pm-grid > .card{display:flex;flex-direction:column}

/* ---- support tickets (/tickets) — was inline in tickets.php ---- */
.tk-grid{display:grid;grid-template-columns:1fr 1.3fr;gap:1rem;align-items:start}
.tk-head{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid var(--border)}
.tk-subject{font-family:var(--display),'Space Grotesk';font-weight:700;font-size:1.2rem}
.tk-thread{display:flex;flex-direction:column;gap:.8rem;max-height:60vh;overflow-y:auto;padding:.3rem}
.tk-msg{display:flex}
.tk-msg.me{justify-content:flex-end}
.tk-bubble{max-width:78%;padding:10px 14px;border-radius:14px;border:1px solid var(--border)}
.tk-msg.me .tk-bubble{background:rgba(233,196,106,.10);border-color:var(--border-2,var(--border));border-bottom-right-radius:4px}
.tk-msg.staff .tk-bubble{background:var(--surface-2,rgba(255,255,255,.04));border-bottom-left-radius:4px}
.tk-meta{font-size:.72rem;color:var(--muted);margin-bottom:.35rem}
.tk-text{line-height:1.6;white-space:pre-wrap;word-break:break-word}
.tk-att{margin-top:.5rem}
.tk-quote{display:block;border-left:3px solid var(--accent);background:rgba(233,196,106,.06);padding:5px 10px;border-radius:0 8px 8px 0;margin-bottom:.4rem;font-size:.82rem;color:var(--muted);text-decoration:none}
.tk-quote-who{color:var(--accent);font-weight:600;margin-right:6px}
.tk-replybtn{background:none;border:none;color:var(--muted);font-size:.72rem;cursor:pointer;padding:.2rem 0 0;margin-top:.3rem}
.tk-replybtn:hover{color:var(--accent)}
.tk-replyquote{display:flex;align-items:center;justify-content:space-between;gap:.6rem;border-left:3px solid var(--accent);background:rgba(233,196,106,.08);padding:6px 10px;border-radius:0 8px 8px 0;margin-bottom:.5rem;font-size:.83rem}
.tk-replyquote button{background:none;border:none;color:var(--muted);cursor:pointer;font-size:1rem}
/* upload progress overlay */
#tkProg{position:fixed;inset:0;background:rgba(7,9,12,.75);backdrop-filter:blur(4px);display:none;align-items:center;justify-content:center;z-index:9999}
#tkProg .box{background:var(--surface);border:1px solid var(--border);border-radius:18px;padding:30px 40px;text-align:center}
#tkProg svg{transform:rotate(-90deg)}
#tkProg .ring-bg{stroke:rgba(255,255,255,.1)}
#tkProg .ring-fg{stroke:var(--accent);stroke-linecap:round;transition:stroke-dashoffset .2s var(--ease)}
#tkProg .pct{font-family:var(--display),'Space Grotesk';font-weight:700;font-size:1.4rem;margin-top:.6rem}
#tkProg .lbl{color:var(--muted);font-size:.9rem;margin-top:.2rem}
.tk-img{max-width:260px;max-height:260px;border-radius:10px;display:block;cursor:zoom-in}
.tk-vid{max-width:320px;max-height:280px;border-radius:10px;display:block}
.tk-reply{margin-top:1rem;border:1px solid var(--border);border-radius:12px;padding:.8rem;background:var(--surface)}
.tk-reply textarea{width:100%;border:none;background:none;resize:vertical;color:var(--text);outline:none;font-family:inherit}
.tk-reply-actions{display:flex;align-items:center;gap:.6rem;margin-top:.5rem;flex-wrap:wrap}
.tk-file{cursor:pointer;margin:0}
.tk-list{display:flex;flex-direction:column;gap:.6rem}
.tk-item{display:flex;align-items:center;gap:.6rem;padding:12px 14px;border:1px solid var(--border);border-radius:12px;text-decoration:none;color:inherit;transition:.18s var(--ease)}
.tk-item:hover{border-color:var(--accent);transform:translateY(-1px)}
.tk-item.tk-unread{border-color:var(--accent);background:rgba(233,196,106,.06)}
.tk-newdot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--accent);margin-right:6px;vertical-align:middle}
.tk-seen{font-size:.7rem;color:var(--accent);margin-top:.2rem;text-align:right}
.tk-item-subj{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media(max-width:820px){.tk-grid{grid-template-columns:1fr}}

/* ---- admin overview (/admin) — was inline in admin/index.php, scoped to the admin area ---- */
.is-admin .dash-head{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;margin-bottom:1.4rem;flex-wrap:wrap}.is-admin .dash-title{font-size:1.5rem;margin:0 0 .2rem}.is-admin .dash-sub{margin:0;font-size:.95rem}.is-admin .stat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin-bottom:1rem}.is-admin .stat-card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:14px 16px;display:flex;align-items:center;gap:14px;transition:.2s var(--ease)}.is-admin .stat-card:hover{border-color:var(--border-2,var(--border));transform:translateY(-2px)}.is-admin .stat-card.stat-alert{border-color:rgba(255,165,90,.5)}.is-admin .stat-ic{width:40px;height:40px;border-radius:11px;display:flex;align-items:center;justify-content:center;font-size:1.2rem;flex:none}.is-admin .stat-meta{flex:1;min-width:0}.is-admin .stat-k{color:var(--muted);font-size:.8rem;margin-bottom:.1rem}.is-admin .stat-v{font-family:var(--display);font-weight:700;font-size:1.35rem;letter-spacing:-.02em;line-height:1.1}.is-admin .stat-cta{font-size:.78rem;color:var(--accent);font-weight:600;text-decoration:none;white-space:nowrap;flex:none;align-self:center}.is-admin .stat-cta:hover{text-decoration:underline}.is-admin .chart-card{display:flex;flex-direction:column;margin-bottom:1rem}.is-admin .chart-legend{display:inline-flex;flex-wrap:wrap;gap:14px;font-size:.8rem;font-weight:500;color:var(--muted)}.is-admin .chart-legend .lg-item{display:inline-flex;align-items:center;gap:6px}.is-admin .chart-legend .lg-dot{width:9px;height:9px;border-radius:50%;flex:none}.is-admin .chart-box{position:relative;height:320px;margin-top:.6rem}.is-admin #chartTT{position:absolute;pointer-events:none;opacity:0;transition:opacity .15s var(--ease);background:rgba(10,12,16,.96);border:1px solid var(--border);border-radius:12px;padding:12px 14px;min-width:200px;box-shadow:0 20px 50px -20px rgba(0,0,0,.8);backdrop-filter:blur(8px);z-index:10;font-size:.86rem}.is-admin #chartTT .tt-day{font-weight:700;margin-bottom:8px;font-size:.95rem;display:flex;justify-content:space-between;gap:10px}.is-admin #chartTT .tt-day .tot{color:var(--accent)}.is-admin #chartTT .tt-row{display:flex;align-items:center;gap:8px;padding:4px 0}.is-admin #chartTT .tt-row .dot{width:9px;height:9px;border-radius:3px;flex:none}.is-admin #chartTT .tt-row .lab{color:var(--muted)}.is-admin #chartTT .tt-row .bar{width:44px;height:5px;border-radius:3px;background:rgba(255,255,255,.08);overflow:hidden;flex:none}.is-admin #chartTT .tt-row .bar>i{display:block;height:100%;border-radius:3px}.is-admin #chartTT .tt-row .val{margin-left:auto;font-weight:600;white-space:nowrap}.is-admin .live-dot{display:inline-block;width:9px;height:9px;border-radius:50%;background:var(--success);margin-left:.4rem;animation:livepulse 1.6s infinite}@keyframes livepulse{0%{box-shadow:0 0 0 0 rgba(45,212,167,.5)}70%{box-shadow:0 0 0 8px rgba(45,212,167,0)}100%{box-shadow:0 0 0 0 rgba(45,212,167,0)}}@media(max-width:1000px){.is-admin .stat-grid{grid-template-columns:1fr 1fr}}@media(max-width:600px){.is-admin .stat-grid{grid-template-columns:1fr}}

/* ---- the admin user list (/admin/users.php) — was inline in admin/users.php, scoped to the admin area ---- */
.is-admin .user-actions{display:flex;flex-wrap:wrap;gap:6px;align-items:center;min-width:230px}.is-admin .user-actions form{margin:0}.is-admin .user-actions .btn{padding:5px 11px;font-size:.82rem}

/* ---- the admin ticket view (/admin/tickets.php) — was inline in admin/tickets.php, scoped to the admin area ---- */
.is-admin .tk-head{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid var(--border)}.is-admin .tk-subject{font-family:var(--display),'Space Grotesk';font-weight:700;font-size:1.2rem}.is-admin .tk-thread{display:flex;flex-direction:column;gap:.8rem;max-height:60vh;overflow-y:auto;padding:.3rem}.is-admin .tk-msg{display:flex}.is-admin .tk-msg.me{justify-content:flex-end}.is-admin .tk-bubble{max-width:78%;padding:10px 14px;border-radius:14px;border:1px solid var(--border)}.is-admin .tk-msg.me .tk-bubble{background:rgba(233,196,106,.10);border-bottom-right-radius:4px}.is-admin .tk-msg.staff .tk-bubble{background:var(--surface-2,rgba(255,255,255,.04));border-bottom-left-radius:4px}.is-admin .tk-meta{font-size:.72rem;color:var(--muted);margin-bottom:.35rem}.is-admin .tk-text{line-height:1.6;white-space:pre-wrap;word-break:break-word}.is-admin .tk-att{margin-top:.5rem}.is-admin .tk-img{max-width:260px;max-height:260px;border-radius:10px;display:block;cursor:zoom-in}.is-admin .tk-vid{max-width:320px;max-height:280px;border-radius:10px;display:block}.is-admin .tk-quote{display:block;border-left:3px solid var(--accent);background:rgba(233,196,106,.06);padding:5px 10px;border-radius:0 8px 8px 0;margin-bottom:.4rem;font-size:.82rem;color:var(--muted);text-decoration:none}.is-admin .tk-quote-who{color:var(--accent);font-weight:600;margin-right:6px}.is-admin .tk-replybtn{background:none;border:none;color:var(--muted);font-size:.72rem;cursor:pointer;padding:.2rem 0 0;margin-top:.3rem}.is-admin .tk-replybtn:hover{color:var(--accent)}.is-admin .tk-reply{margin-top:1rem;border:1px solid var(--border);border-radius:12px;padding:.8rem;background:var(--surface)}.is-admin .tk-reply textarea{width:100%;border:none;background:none;resize:vertical;color:var(--text);outline:none;font-family:inherit}.is-admin .tk-reply-actions{display:flex;align-items:center;gap:.6rem;margin-top:.5rem;flex-wrap:wrap}.is-admin .tk-file{cursor:pointer;margin:0}.is-admin .tk-replyquote{display:flex;align-items:center;justify-content:space-between;gap:.6rem;border-left:3px solid var(--accent);background:rgba(233,196,106,.08);padding:6px 10px;border-radius:0 8px 8px 0;margin-bottom:.5rem;font-size:.83rem}.is-admin .tk-replyquote button{background:none;border:none;color:var(--muted);cursor:pointer;font-size:1rem}.is-admin .tk-newdot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--accent);vertical-align:middle}.is-admin #aProg{position:fixed;inset:0;background:rgba(7,9,12,.75);backdrop-filter:blur(4px);display:none;align-items:center;justify-content:center;z-index:9999}.is-admin #aProg .box{background:var(--surface);border:1px solid var(--border);border-radius:18px;padding:30px 40px;text-align:center}.is-admin #aProg svg{transform:rotate(-90deg)}.is-admin #aProg .ring-bg{stroke:rgba(255,255,255,.1)}.is-admin #aProg .ring-fg{stroke:var(--accent);stroke-linecap:round;transition:stroke-dashoffset .2s var(--ease)}.is-admin #aProg .pct{font-family:var(--display),'Space Grotesk';font-weight:700;font-size:1.4rem;margin-top:.6rem}.is-admin #aProg .lbl{color:var(--muted);font-size:.9rem;margin-top:.2rem}

/* ---- server status (/admin/server.php) — was inline in admin/server.php, scoped to the admin area ---- */
.is-admin .srv-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin-bottom:1rem}.is-admin .srv-grid2{display:grid;grid-template-columns:1fr 1fr;gap:1rem}.is-admin .srv-card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:20px;text-align:center}.is-admin .srv-h{color:var(--muted);font-size:.85rem;margin-bottom:.8rem}.is-admin .ring-wrap{position:relative;width:140px;height:140px;margin:0 auto}.is-admin .ring{width:140px;height:140px;transform:rotate(-90deg)}.is-admin .ring .rb{fill:none;stroke:rgba(255,255,255,.08);stroke-width:9}.is-admin .ring .rf{fill:none;stroke:var(--accent);stroke-width:9;stroke-linecap:round;stroke-dasharray:326.7;stroke-dashoffset:326.7;transition:stroke-dashoffset .5s var(--ease), stroke .5s var(--ease)}.is-admin .ring-txt{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:var(--display),'Space Grotesk';font-weight:700;font-size:1.8rem}.is-admin .srv-sub{color:var(--muted);font-size:.85rem;margin-top:.6rem}.is-admin .kv{display:flex;flex-direction:column;gap:.55rem}.is-admin .kv .row{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem;border-bottom:1px solid var(--border);padding-bottom:.5rem}.is-admin .kv .row:last-child{border-bottom:none}.is-admin .kv .row .k{color:var(--muted)}.is-admin .kv .row .v{font-weight:600;font-family:var(--display),'Space Grotesk'}.is-admin .dot-ok{color:#2dd4a7}.is-admin .dot-bad{color:#ff6b6b}@media(max-width:820px){.is-admin .srv-grid{grid-template-columns:1fr}.is-admin .srv-grid2{grid-template-columns:1fr}}

/* ---- the sign-in animation (includes/_welcome.php) ---------------------------------------
   On the login page ONLY THE CARD moves: it closes from its top and bottom edges towards the
   middle until they meet and it is a single lit line, with the page around it untouched. On
   the page landed on afterwards the brand fades onto that line a letter at a time, the four
   platform marks fade in under it one by one, each beats once in turn, and it clears.

   Timeline on landing:
     0.00  letters fade in, 90ms apart
     0.60  marks fade in under the line, 130ms apart
     1.35  each mark beats once, 150ms apart
     2.05  everything fades; the overlay stops existing to the pointer at 2.55

   Everything that matters is CSS. Each step uses `animation-fill-mode:forwards`, so the
   overlay stops covering the page even with scripting disabled - verified with
   java_script_enabled=False. assets/auth-welcome.js only removes the dead node afterwards.
   An overlay that needs JavaScript to go away is one browser quirk from locking someone out
   of their own account.                                                                     */
/* The page landed on shows the build ALREADY DONE - the login page ran it - so nothing
   re-animates and the swap has nothing to restart. Only the name, which is the one thing that
   had to wait for the server, fades in; then it clears. */
.wlc{position:fixed;inset:0;z-index:10000;background:var(--bg,#0b0e13);
  animation:wlcFade .45s ease 1.05s forwards}
.wlc:not(.is-pending) .wlc-word > span{opacity:1;animation:none;transform:translateY(-.52em)}
.wlc:not(.is-pending) .wlc-mark{opacity:1;
  animation:wlcBeat .5s cubic-bezier(.3,0,.2,1) calc(.15s + var(--j) * .13s)}
.wlc:not(.is-pending) .wlc-msg{animation:wlcUp .35s ease .1s forwards}
.wlc-beam{position:absolute;left:50%;top:50%;width:min(78vw,520px);height:2px;
  transform:translate(-50%,-50%) scaleX(1);transform-origin:50% 50%;
  background:linear-gradient(90deg,transparent,var(--accent,#E9C46A),transparent);
  box-shadow:0 0 26px 1px rgba(233,196,106,.55)}
.wlc-word{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  /* the brand is a Latin wordmark and the letters are separate flex items, so an RTL page
     would lay them out right to left and spell it backwards - measured as "XULGT" in Persian */
  display:flex;direction:ltr;gap:.34em;
  font-family:var(--display),'Space Grotesk',sans-serif;font-weight:700;
  font-size:clamp(26px,7vw,44px);letter-spacing:.06em;color:var(--text,#e8eaf0)}
/* each letter simply fades up onto the line in turn - no sliding, no overshoot */
.wlc-word > span{display:inline-block;opacity:0;transform:translateY(-.52em);
  animation:wlcLetter .42s ease forwards;animation-delay:calc(var(--i) * .09s)}
@keyframes wlcLetter{from{opacity:0}to{opacity:1}}

/* the platform marks, under the line */
.wlc-marks{position:absolute;left:50%;top:calc(50% + 2.6rem);transform:translateX(-50%);
  display:flex;direction:ltr;gap:1.5rem;align-items:center}
.wlc-mark{display:inline-flex;color:var(--mc);opacity:0;line-height:0;
  animation:wlcMarkIn .45s ease forwards calc(.6s + var(--j) * .13s),
            wlcBeat .5s cubic-bezier(.3,0,.2,1) calc(1.35s + var(--j) * .15s)}
.wlc-mark svg{display:block;filter:drop-shadow(0 0 10px currentColor)}
@keyframes wlcMarkIn{from{opacity:0}to{opacity:1}}
/* one beat each, in turn: a heart gives a big push and a smaller one after it */
@keyframes wlcBeat{0%{transform:scale(1)}22%{transform:scale(1.32)}44%{transform:scale(1)}
                   62%{transform:scale(1.16)}100%{transform:scale(1)}}

.wlc-msg{position:absolute;left:50%;top:calc(50% + 5.6rem);transform:translateX(-50%);
  margin:0;width:max-content;max-width:88vw;text-align:center;
  font-family:var(--display),'Space Grotesk',sans-serif;font-weight:600;font-size:1rem;
  color:var(--muted,#8b93a7);opacity:0;animation:wlcUp .4s ease 1.05s forwards}
@keyframes wlcUp{from{opacity:0;transform:translateX(-50%) translateY(8px)}
                 to{opacity:1;transform:translateX(-50%) translateY(0)}}
@keyframes wlcFade{to{opacity:0;visibility:hidden;pointer-events:none}}

/* Still checking the credentials. NOTHING covers the page here - the overlay is transparent
   and carries only the line the card is closing onto, so the close is what you actually see. */
/* ---- while the credentials are being checked ---------------------------------------------
   The build - beam, then letters, then marks - runs HERE, on the login page, starting the
   moment the line forms. It used to run on the page landed on afterwards, which meant it could
   not start until the server had answered: on a phone that reads as the site stalling on a bare
   line and then jumping. Nothing here claims success; the brand and the platform marks are not
   a statement that the password was right. The one thing that does claim it - the name - is
   still only written after the server says so.

   Transparent at first so the card closing is visible, then darkening once it is a line, so by
   the time the answer arrives the screen already matches the page it is about to become and
   the swap is invisible. */
.wlc.is-pending{background:transparent;pointer-events:none;
  animation:wlcDarken .45s ease .42s forwards}
@keyframes wlcDarken{to{background:var(--bg,#0b0e13)}}
.wlc.is-pending .wlc-beam{transform:translate(-50%,-50%) scaleX(0);
  animation:wlcBeamIn .4s cubic-bezier(.2,.9,.3,1) .34s forwards}
.wlc.is-pending .wlc-word > span{animation-delay:calc(.72s + var(--i) * .09s)}
.wlc.is-pending .wlc-mark{animation:wlcMarkIn .45s ease forwards calc(1.2s + var(--j) * .13s),
                                    wlcBeat .5s cubic-bezier(.3,0,.2,1) calc(1.9s + var(--j) * .15s)}
.wlc.is-pending .wlc-msg{animation:wlcUp .4s ease 1.05s forwards}
@keyframes wlcBeamIn{to{transform:translate(-50%,-50%) scaleX(1)}}
@keyframes wlcBeamPulse{0%,100%{opacity:1}50%{opacity:.45}}

/* ---- the server answered mid-build --------------------------------------------------------
   The build above runs on the login page and takes about 1.8s; a fast reply arrives long before
   that. Without this the page landed on jumped straight to the finished picture, so on a quick
   connection the letters and the marks never appeared one at a time at all - the thing that was
   asked for - and on a middling one three letters became five letters and four logos in a frame.

   `--wlc-off` is minus however long the overlay had ALREADY been running when the login page
   went away, so every delay here is measured from the moment it first appeared and the build
   simply carries on across the navigation. Same keyframes, same timings, one offset.

   The heartbeat is floored at 0s rather than offset past: on a slow reply it would otherwise
   have been due while the browser was still swapping pages, and it would be missed entirely.

   With no JavaScript, no sessionStorage or an implausible gap there is no `is-resume` and the
   finished-state rules further up stand - which is exactly the old behaviour. */
.wlc.is-resume{animation-delay:max(.6s,calc(2.5s + var(--wlc-off,0s)))}
.wlc.is-resume .wlc-word > span{opacity:0;animation:wlcLetter .42s ease forwards;
  animation-delay:calc(.72s + var(--i) * .09s + var(--wlc-off,0s))}
.wlc.is-resume .wlc-mark{opacity:0;
  animation:wlcMarkIn .45s ease forwards calc(1.2s + var(--j) * .13s + var(--wlc-off,0s)),
            wlcBeat .5s cubic-bezier(.3,0,.2,1)
                    max(0s,calc(1.9s + var(--j) * .15s + var(--wlc-off,0s)))}

/* ---- the card closing ---------------------------------------------------------------------
   Both edges travel inward at the same rate - `transform-origin:50% 50%` - rather than the
   bottom rising to a fixed top.

   ONE keyframe from start to finish, deliberately. An intermediate step at 55% made the easing
   restart there and the close visibly hitched in the middle: the curve is applied to each
   segment, not to the whole animation. */
body.wlc-folding .auth-card .card{transform-origin:50% 50%;will-change:transform;
  animation:wlcClose .46s cubic-bezier(.6,.02,.78,.06) forwards}
/* a highlight along the seam, brightest just as the two halves meet */
body.wlc-folding .auth-card .card::after{content:"";position:absolute;left:0;right:0;top:50%;
  height:2px;transform:translateY(-50%);pointer-events:none;border-radius:2px;
  background:linear-gradient(90deg,transparent,var(--accent,#E9C46A),transparent);
  opacity:0;animation:wlcSeam .46s ease forwards}
body.wlc-folding .auth-card .card{position:relative;overflow:hidden}
@keyframes wlcClose{from{transform:scaleY(1)}to{transform:scaleY(.004);opacity:.75}}
@keyframes wlcSeam{0%{opacity:0}60%{opacity:.75}90%{opacity:1}100%{opacity:0}}

@media (prefers-reduced-motion:reduce){
  body.wlc-folding .auth-card .card{animation:none;opacity:0}
  body.wlc-folding .auth-card .card::after{animation:none;opacity:0}
  .wlc-word > span,.wlc.is-resume .wlc-word > span{animation:none;opacity:1}
  .wlc-mark,.wlc.is-resume .wlc-mark{animation:wlcMarkIn .01s linear forwards}
  .wlc.is-resume{animation:wlcFade .3s ease .9s forwards;animation-delay:.9s}
  .wlc-msg{animation:wlcUp .01s linear forwards}
  .wlc{animation:wlcFade .3s ease .9s forwards}
  .wlc.is-pending .wlc-beam{animation:none;transform:translate(-50%,-50%) scaleX(1)}
}

/* ---- motion: the things that had no transition at all -------------------------------------
   Selects, textareas, tabs, links and table rows changed state instantly, which is most of why
   the site felt like it snapped rather than moved. Everything below eases with the shared
   tokens, so there is one curve and three durations across the whole interface.

   Only `color`, `background`, `border-color`, `box-shadow`, `opacity` and `transform` are
   transitioned - never a layout property, and never `all`. `all` will happily animate something
   that forces layout on every frame, which is the usual reason a "smooth" stylesheet stutters. */
a{transition:color var(--dur-1) var(--ease-soft),opacity var(--dur-1) var(--ease-soft)}
select,textarea,input{transition:border-color var(--dur-2) var(--ease),
  box-shadow var(--dur-2) var(--ease),background var(--dur-2) var(--ease-soft)}
.tabbar .tab{transition:color var(--dur-2) var(--ease),background var(--dur-2) var(--ease),
  border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease)}
tr td{transition:background var(--dur-1) var(--ease-soft)}
.badge,.linkish,summary{transition:color var(--dur-1) var(--ease-soft),
  background var(--dur-1) var(--ease-soft),opacity var(--dur-1) var(--ease-soft)}

/* Press feedback. A button that only changes colour feels like a picture of a button; one that
   gives under the finger feels like a control. Kept to 2% - more reads as a bounce. */
.btn:active,.linkish:active,.tabbar .tab:active{transform:scale(.98)}

/* Focus rings for keyboard users, eased in rather than appearing hard-edged. */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Anchor jumps and scrollIntoView glide instead of teleporting. */
html{scroll-behavior:smooth}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .btn:active,.linkish:active,.tabbar .tab:active{transform:none}
}

/* ---- TGLUX captcha -------------------------------------------------------------------
   The checkbox, then the puzzle panel: picture, slider, footer. Laid out like the slider
   captchas people already recognise, in this site's own colours.

   Everything is sized in % of the panel, and the picture is given an `aspect-ratio` rather
   than a height, so the same markup works at 300px and at 360px-minus-padding without the
   slider and the piece ever disagreeing about where they are. */
.tgcap{margin:0 0 1rem}
.tgcap-box{display:flex;align-items:center;gap:.7rem;padding:.7rem .8rem;cursor:pointer;
  border:1px solid var(--border,#232a36);border-radius:12px;background:var(--card-2,#111722);
  transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease)}
.tgcap-box:hover{border-color:var(--accent,#E9C46A)}
/* Solved: it stays solved, so it must stop looking like something to press. */
.tgcap.is-done .tgcap-box{border-color:var(--success,#3ddc97);cursor:default}
.tgcap.is-done .tgcap-check{cursor:default}
.tgcap.is-done .tgcap-box:hover{border-color:var(--success,#3ddc97)}
.tgcap-check{flex:0 0 auto;width:26px;height:26px;border-radius:6px;cursor:pointer;
  border:2px solid var(--muted,#8b93a7);background:transparent;display:inline-flex;
  align-items:center;justify-content:center;
  transition:border-color var(--dur-2) var(--ease),background var(--dur-2) var(--ease)}
.tgcap-check:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.tgcap-check i{width:13px;height:8px;border-left:2.5px solid #0b0e13;border-bottom:2.5px solid #0b0e13;
  transform:rotate(-45deg) scale(0);transform-origin:50% 50%;opacity:0;
  transition:transform var(--dur-3) var(--ease),opacity var(--dur-1) var(--ease-soft)}
.tgcap.is-done .tgcap-check{background:var(--success,#3ddc97);border-color:var(--success,#3ddc97)}
.tgcap.is-done .tgcap-check i{transform:rotate(-45deg) scale(1);opacity:1}
/* Busy: the tick box becomes a spinner, so the wait is on the control that was pressed. */
.tgcap.is-busy .tgcap-check{border-color:var(--accent,#E9C46A);border-top-color:transparent;
  border-radius:50%;animation:tgcapSpin .7s linear infinite}
.tgcap.is-busy .tgcap-check i{opacity:0}
@keyframes tgcapSpin{to{transform:rotate(360deg)}}
.tgcap-label{flex:1 1 auto;min-width:0;font-size:.95rem}
.tgcap-brand{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;line-height:1.05;
  color:var(--muted,#8b93a7)}
.tgcap-brand img{display:block;width:26px;height:26px;margin-bottom:2px}
.tgcap-brand b{font-family:var(--display),'Space Grotesk',sans-serif;font-size:.62rem;
  letter-spacing:.09em;color:var(--accent,#E9C46A)}
.tgcap-brand span{font-size:.55rem;letter-spacing:.06em;text-transform:uppercase}
.tgcap-msg{min-height:1.1em;margin-top:.4rem;font-size:.82rem;color:var(--muted,#8b93a7)}
.tgcap-msg.is-bad{color:var(--danger,#ff6b6b)}
.tgcap-msg.is-ok{color:var(--success,#3ddc97)}

/* The puzzle opens OVER the page, centred, the way every captcha of this shape does -
   rather than expanding inside the form and pushing the button it belongs to off screen.
   It is appended to <body>, so `position:fixed` cannot be captured by a transformed
   ancestor (the sign-in animation puts a transform on this very card). */
/* Above the floating contact bubble and the chat widget, which both sit at 9999 - a button
   hovering over a modal is not dimmed, is still tappable, and lands right where a thumb
   goes. Below the sign-in overlay at 10000, which comes after this and must cover it. */
.tgcap-modal{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;
  justify-content:center;padding:16px}
.tgcap-scrim{position:absolute;inset:0;background:rgba(4,6,10,.74);
  animation:tgcapScrim var(--dur-2) var(--ease-soft) both}
@supports (backdrop-filter:blur(3px)){.tgcap-scrim{backdrop-filter:blur(3px)}}
@keyframes tgcapScrim{from{opacity:0}to{opacity:1}}
/* The page must not scroll behind the puzzle - on a phone the drag would otherwise pan it. */
body.tgcap-open{overflow:hidden}

.tgcap-panel{position:relative;width:100%;max-width:330px;padding:.85rem;border-radius:16px;
  border:1px solid var(--border,#232a36);background:var(--card-2,#111722);
  box-shadow:0 24px 60px rgba(0,0,0,.6);animation:tgcapIn var(--dur-3) var(--ease) both}
@keyframes tgcapIn{from{opacity:0;transform:translateY(10px) scale(.97)}
                   to{opacity:1;transform:none}}
/* Short screens: the picture and the slider must stay on screen, so the card scrolls
   inside itself rather than the page scrolling under the scrim. */
@media (max-height:560px){
  .tgcap-modal{align-items:flex-start;overflow:auto}
  .tgcap-panel{margin:8px 0}
}
.tgcap-note{min-height:1.1em;margin-top:.5rem;font-size:.82rem;text-align:center;
  color:var(--muted,#8b93a7)}
.tgcap-note.is-bad{color:var(--danger,#ff6b6b)}
.tgcap-note.is-ok{color:var(--success,#3ddc97)}
.tgcap-head{font-size:.85rem;margin-bottom:.55rem;text-align:center;color:var(--text,#e8eaf0)}
.tgcap-stage{position:relative;width:100%;overflow:hidden;border-radius:10px;
  background:#0b0e13;touch-action:none;user-select:none}
.tgcap-stage.is-wrong{animation:tgcapShake .4s var(--ease-soft)}
@keyframes tgcapShake{0%,100%{transform:translateX(0)}20%{transform:translateX(-7px)}
  45%{transform:translateX(6px)}70%{transform:translateX(-3px)}}
.tgcap-bg{display:block;width:100%;height:auto}
.tgcap-piece{position:absolute;top:0;left:0;height:100%;pointer-events:none;
  filter:drop-shadow(0 2px 6px rgba(0,0,0,.65))}
.tgcap.is-dragging .tgcap-piece{filter:drop-shadow(0 3px 10px rgba(0,0,0,.8))}

.tgcap-track{position:relative;height:40px;margin-top:.6rem;border-radius:20px;
  background:var(--bg,#0b0e13);border:1px solid var(--border,#232a36);touch-action:none}
.tgcap-fill{position:absolute;left:0;top:0;bottom:0;width:40px;border-radius:20px;
  background:linear-gradient(90deg,rgba(233,196,106,.18),rgba(233,196,106,.34));pointer-events:none}
.tgcap-handle{position:absolute;left:0;top:-1px;width:52px;height:40px;border-radius:20px;
  display:flex;align-items:center;justify-content:center;cursor:grab;touch-action:none;
  color:#0b0e13;background:var(--accent,#E9C46A);
  box-shadow:0 2px 10px rgba(0,0,0,.45);
  /* transform is the drag position and must NOT be eased, or the piece lags the finger */
  transition:box-shadow var(--dur-2) var(--ease)}
.tgcap-handle:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.tgcap.is-dragging .tgcap-handle{cursor:grabbing;box-shadow:0 4px 16px rgba(0,0,0,.6)}

.tgcap-foot{display:flex;align-items:center;gap:.45rem;margin-top:.55rem}
.tgcap-ico{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;
  padding:0;border-radius:8px;cursor:pointer;color:var(--muted,#8b93a7);
  border:1px solid var(--border,#232a36);background:transparent;
  transition:color var(--dur-1) var(--ease-soft),border-color var(--dur-1) var(--ease-soft)}
.tgcap-ico:hover{color:var(--accent,#E9C46A);border-color:var(--accent,#E9C46A)}
.tgcap-mark{margin-inline-start:auto;display:flex;align-items:baseline;gap:.3rem;
  color:var(--muted,#8b93a7);font-size:.6rem;letter-spacing:.06em;text-transform:uppercase}
.tgcap-mark b{font-family:var(--display),'Space Grotesk',sans-serif;font-size:.7rem;
  letter-spacing:.09em;color:var(--accent,#E9C46A);text-transform:none}

/* The panel is a picture with a slider under it; RTL must not mirror it, or the piece
   would travel away from the hole. */
[dir="rtl"] .tgcap-panel{direction:ltr}
[dir="rtl"] .tgcap-head,[dir="rtl"] .tgcap-msg{direction:rtl}

@media (prefers-reduced-motion:reduce){
  .tgcap-panel,.tgcap-scrim{animation:none}
  .tgcap-stage.is-wrong{animation:none;outline:2px solid var(--danger,#ff6b6b)}
  .tgcap.is-busy .tgcap-check{animation:none}
}

/* The "keep me signed in" line on the auth forms. `width:auto` because the shared input
   rule stretches every input to the full field width, which would give a checkbox a 300px
   box. */
.checkline{display:flex;align-items:center;gap:.55rem;margin:0 0 1rem;
  font-size:.9rem;color:var(--muted,#8b93a7);cursor:pointer}
.checkline input[type=checkbox]{width:auto;margin:0;accent-color:var(--accent,#E9C46A);cursor:pointer}

/* ---- long tables: skip the work for rows nobody is looking at ---------------------------
   /admin/services renders 500 services, and a service row is five separate forms - 46 tags,
   3.5 KB, and 24,000 nodes on the page. Measured on a phone-class CPU: 2.1s to
   DOMContentLoaded, 4.3s to load and 3.1s of BLOCKED MAIN THREAD, against ~500ms for every
   other page on the site. That is the lag.

   `content-visibility:auto` lets the browser skip layout and paint for rows outside the
   viewport and do them as they scroll in. The markup, the row count and every action on the
   page are untouched. Measured after: load 4254ms -> 1196ms, blocked 3154ms -> 235ms.

   It beat cutting the list to 100 rows (1602ms / 710ms) AND keeps all 500 visible, so there
   is nothing to page through and no workflow to relearn.

   `contain-intrinsic-size: auto <h>` is what stops the scrollbar jumping: the browser uses
   the guess until a row has been rendered once, then remembers that row's real height.
   Unsupported browsers ignore the whole declaration and get exactly what they get today. */
/* The placeholder height has to be close to the real one or the page grows under the
   scrollbar as rows render. A row is ~146px wide-screen and ~460px once it stacks on a
   phone, so the guess is given per breakpoint; `auto` then replaces it with the row's real
   height the first time that row is rendered. Measured page height on a phone: 252,908px
   before, 66,168px with a 96px guess (a scrollbar that lies), 246,000px with 460px. */
.stack-table tbody tr{content-visibility:auto;contain-intrinsic-size:auto 146px}
@media (max-width:760px){.stack-table tbody tr{contain-intrinsic-size:auto 460px}}
/* Printing must not skip what has never been scrolled into view. */
@media print{.stack-table tbody tr{content-visibility:visible}}

/* Crawlable language list (includes/lang_nav.php). Plain links so search engines can reach
   every language version; the <select> switchers are kept for people. */
.lang-nav{margin:1.2rem auto 0;max-width:1100px;padding:0 1rem}
.lang-nav ul{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.35rem .9rem;justify-content:center;font-size:.85rem}
.lang-nav a{color:var(--muted,#9aa3b2);text-decoration:underline;text-underline-offset:2px}
.lang-nav a:hover{color:var(--accent,#E9C46A)}
.lang-nav [aria-current]{color:var(--text,#e8e8e8);font-weight:600}

/* A plain list of related internal links (related articles under a post, guides on a category
   page). Real <a href>s: they are what gives a new post or a translated page its inlinks. */
.rel-links{margin:2rem 0 0;padding:1.1rem 1.2rem;border:1px solid var(--border);border-radius:14px;background:var(--surface)}
.rel-links h2,.rel-links h3{font-size:1.05rem;margin:0 0 .6rem}
.rel-links ul{list-style:none;margin:0;padding:0;display:grid;gap:.45rem}
.rel-links a{color:var(--text);text-decoration:underline;text-underline-offset:2px}
.rel-links a:hover{color:var(--accent)}

/* /catalog: link to the category page under each accordion table (2026-10-14) */
.acc-link{margin:.6rem .25rem .1rem}

/* /services/<category>: related categories under the intro (2026-10-14) */
.svc-see{margin:-.4rem 0 1rem}
.svc-see a{white-space:normal}

/* Unread ticket replies (2026-10-15): a count at the end of the "Support" link, and a dot on the
   phone menu button, since on a phone the menu is closed. margin-inline-start keeps the count at
   the far end in RTL too. */
.nav-badge{margin-inline-start:auto;min-width:20px;height:20px;padding:0 6px;border-radius:999px;
  background:#c62f45;color:#fff;font-size:11px;font-weight:700;line-height:20px;text-align:center;flex:none}
.mobile-toggle{position:relative}
.nav-dot{position:absolute;top:2px;inset-inline-end:2px;width:9px;height:9px;border-radius:50%;
  background:var(--danger);box-shadow:0 0 0 2px var(--bg);pointer-events:none}

/* Service description in the catalogue (2026-10-15): a native <details> under the service name. */
.svc-details{margin-top:.35rem}
.svc-details summary{cursor:pointer;font-size:12px;color:var(--accent);list-style:none;display:inline-block}
.svc-details summary::-webkit-details-marker{display:none}
.svc-details summary::before{content:"ⓘ ";}
.svc-details[open] summary{margin-bottom:.35rem}
.svc-details .svc-desc-body{font-size:13px;max-width:520px;padding:.6rem .8rem;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--bg-2)}
/* On a phone the catalogue table scrolls sideways; give the service-name column room instead of
   squeezing it to one word per line (worse once a category has the average-time column). */
@media (max-width:760px){.acc-body table td:nth-child(2){min-width:190px}}
.svc-desc-body[dir=auto]{text-align:start}

/* /tickets on a phone (2026-10-15): the grid item's min-content width (a row with priority,
   status AND the "New reply" badge) pushed the page sideways. Same grid min-width bug as .main. */
.tk-grid > *{min-width:0}
/* The subject block takes a line of its own on a phone and the badges wrap under it. */
.tk-item-main{flex:1;min-width:0}
@media(max-width:560px){.tk-item{flex-wrap:wrap}.tk-item-main{flex:1 1 100%}}

/* ---- admin ticket moderation (2026-10-19): filters, bulk bar, per-ticket actions ---- */
.tk-filters{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin-bottom:.8rem}
.tk-filter-user{display:inline-flex;align-items:center;gap:.4rem;margin-inline-start:.4rem}
.tk-bulkbar{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin-bottom:.7rem;padding:.55rem .7rem;
  border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-2,rgba(255,255,255,.03))}
.tk-bulkbar .checkline{display:inline-flex;align-items:center;gap:.4rem;margin:0;cursor:pointer}
.tk-selcount{margin-inline-end:auto}
.tk-bulkbar button[disabled]{opacity:.45;cursor:not-allowed}
.tk-selcell{display:inline-flex;align-items:center;justify-content:center;min-width:28px;min-height:28px;margin:0;cursor:pointer}
input.tk-sel,#tkAll{width:18px;height:18px;padding:0;margin:0;accent-color:var(--accent);cursor:pointer}
.tk-mod{display:flex;flex-wrap:wrap;gap:.6rem 1rem;align-items:center;justify-content:space-between;
  margin-top:1rem;padding-top:1rem;border-top:1px solid var(--border)}
.tk-mod-who{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
.tk-mod-actions{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center}
.tk-mod-actions form{margin:0}
.tk-link{text-decoration:underline;text-decoration-color:var(--accent);text-underline-offset:2px}
.tk-link:hover{color:var(--accent)}
.tk-selword{display:none}
/* The label's hit area covers the whole Select cell at every width, so a near miss ticks the box
   instead of falling through to the row and opening the ticket (which also drops the selection). */
.tk-table td.tk-seltd{position:relative}
.tk-table .tk-selcell::after{content:"";position:absolute;inset:0}
.tk-table input.tk-sel{position:relative;z-index:1}
/* On a phone each row is a card. The whole Select line is the label, so a tap on the word
   ticks the box instead of falling through to the row and opening the ticket. */
@media (max-width:760px){
  .tk-table td.tk-seltd{padding:0}
  .tk-table .tk-selcell{display:flex;justify-content:flex-start;gap:.6rem;width:100%;padding:.6rem .8rem}
  .tk-selword{display:inline;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.06em;font-weight:600}
}
/* ---------- Price-cut badge (includes/price_cut.php, 2026-10-26) ----------
   Not "ad-"/"promo"/"sale": ad blockers hide those names. A solid green with white text is
   5.49:1 whatever the theme, because the badge carries its own background. Logical properties
   only, so it sits on the correct side of the price in fa/ar/ur. */
.pcut{display:inline-block;margin-inline-start:.4rem;padding:1px 7px;border-radius:100px;background:#13784e;
  color:#fff;font-size:.72rem;font-weight:700;line-height:1.5;white-space:nowrap;vertical-align:middle}
.pcut[title]{cursor:help}
.pcut-tag{font-weight:600;letter-spacing:.01em}
.acc-name .pcut-tag{font-size:.7rem}
.pcut-note{max-width:720px;margin:.9rem 0 0;padding:.6rem .85rem;border-inline-start:3px solid #1f9d63;
  border-radius:var(--radius-sm,8px);background:rgba(31,157,99,.10);color:var(--text);font-size:.98rem;
  overflow-wrap:anywhere}
.pcut-note .pcut{margin-inline-start:0;margin-inline-end:.45rem}
.pcut-line{color:var(--text);font-weight:600}
/* price + badge wrap together: when the badge drops to its own line it starts flush, with no indent */
.pcut-cell{display:inline-flex;flex-wrap:wrap;align-items:center;gap:.2rem .4rem}
.pcut-cell .pcut{margin-inline-start:0}
.acc-body .pcut-note{margin:.2rem 0 .8rem}
