/* ============================================================
   AstralFate — Cosmic design system
   Shared across all app screens. No frameworks, no CDN.
   Maps 1:1 onto the future askama base template.
   ============================================================ */

/* ---- tokens ---- */
:root{
  /* surface */
  --bg:#0A0518;
  --bg-2:#150A33;
  --panel:rgba(255,255,255,.045);
  --panel-2:rgba(255,255,255,.075);
  --field:rgba(0,0,0,.28);
  --field-line:rgba(167,139,250,.18);
  --panel-solid:#150C2E;
  --line:rgba(167,139,250,.18);
  --line-2:rgba(167,139,250,.34);

  /* brand */
  --violet:#A78BFA;
  --violet-d:#7C3AED;
  --violet-dd:#5B21B6;
  --gold:#F5C542;
  --gold-d:#C99A24;

  /* semantic */
  --ok:#34D399;
  --warn:#FBBF24;
  --danger:#F87171;

  /* text */
  --text:#EDE9FE;
  --muted:#9C93C4;
  --muted-2:#6F679A;
  /* higher-contrast text for long-form reading surfaces */
  --read:#E4DFF7;

  /* shape */
  --r-sm:8px; --r:12px; --r-md:16px; --r-lg:20px; --r-xl:28px;
  --shadow:0 14px 40px rgba(0,0,0,.5);
  --glow:0 6px 26px rgba(124,58,237,.45);

  --sans:"Segoe UI",system-ui,-apple-system,sans-serif;
  --mono:Consolas,"SF Mono",ui-monospace,monospace;
}

/* Light theme. The dark palette above is the base; light applies when the
   visitor forced "light" (`[data-theme="light"]`, any device), OR — the default
   — hasn't forced "dark" and their device is in light mode
   (`:not([data-theme="dark"])` under the media query, which also covers the
   no-attribute default and the explicit "system" choice). Same brand violet and
   gold, darkened where they'd wash out on a bright surface; surfaces and text
   inverted. The vars are repeated for the two selectors rather than shared
   because one is gated on the media query and one is not. */
/* The page width, shared by the top bar and the body under it — see .wrap. */
:root { --wrap:1120px }
/* Pages whose whole point is a big picture: the tree, the shadow tree, the sky.
   Not the reading pages — a 1360px measure is a worse paragraph, not a better
   one. */
body.wide { --wrap:1360px }

:root[data-theme="light"] {
  --bg:#F6F4FD;
  --bg-2:#EBE5FB;
  --panel:rgba(255,255,255,.72);
  --panel-2:rgba(255,255,255,.9);
  --field:rgba(255,255,255,.92);
  --field-line:rgba(124,58,237,.28);
  --panel-solid:#FFFFFF;
  --line:rgba(124,58,237,.16);
  --line-2:rgba(124,58,237,.30);

  --violet:#6D28D9;
  --violet-d:#7C3AED;
  --violet-dd:#5B21B6;
  --gold:#B7791F;
  --gold-d:#8A5A12;

  --ok:#059669;
  --warn:#B45309;
  --danger:#DC2626;

  --text:#1E1B2E;
  --muted:#5B5478;
  --muted-2:#847CA6;
  --read:#2B2640;

  --shadow:0 12px 34px rgba(83,58,148,.15);
  --glow:0 6px 22px rgba(124,58,237,.25);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg:#F6F4FD;
    --bg-2:#EBE5FB;
    --panel:rgba(255,255,255,.72);
    --panel-2:rgba(255,255,255,.9);
    --field:rgba(255,255,255,.92);
    --field-line:rgba(124,58,237,.28);
    --panel-solid:#FFFFFF;
    --line:rgba(124,58,237,.16);
    --line-2:rgba(124,58,237,.30);

    --violet:#6D28D9;
    --violet-d:#7C3AED;
    --violet-dd:#5B21B6;
    --gold:#B7791F;
    --gold-d:#8A5A12;

    --ok:#059669;
    --warn:#B45309;
    --danger:#DC2626;

    --text:#1E1B2E;
    --muted:#5B5478;
    --muted-2:#847CA6;
    --read:#2B2640;

    --shadow:0 12px 34px rgba(83,58,148,.15);
    --glow:0 6px 22px rgba(124,58,237,.25);
  }
}
/* On light surfaces the white star-field and its dark vignette read as dirt;
   drop them and let the lavender background stand on its own. */
:root[data-theme="light"] .bg-stars::before,
:root[data-theme="light"] .bg-stars::after,
:root[data-theme="light"] .bg-calm::before,
:root[data-theme="light"] .bg-calm::after { opacity:0 }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .bg-stars::before,
  :root:not([data-theme="dark"]) .bg-stars::after,
  :root:not([data-theme="dark"]) .bg-calm::before,
  :root:not([data-theme="dark"]) .bg-calm::after { opacity:0 }
}

/* The top bars carry a hardcoded dark tint (they float over the star-field);
   on light, make them frosted-white so they don't sit as a dark slab. */
:root[data-theme="light"] .nav,
:root[data-theme="light"] .appbar { background:rgba(255,255,255,.82) }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .nav,
  :root:not([data-theme="dark"]) .appbar { background:rgba(255,255,255,.82) }
}

/* Theme switcher on the account page. */
.themesw{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.themesw label{display:inline-flex;gap:5px;align-items:center;font-size:13.5px;cursor:pointer;color:var(--read)}
.themesw input{accent-color:var(--violet-d)}
.setform{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.setform .input{min-width:200px}

/* Chat */
.chat-head{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.chat-back{font-size:22px;color:var(--violet);text-decoration:none;line-height:1}
.chat-head .av{width:44px;height:44px;flex:0 0 auto}
.chat-who b{display:block;font-size:16px}
.chat-who a{font-size:12.5px}
.chat-thread{display:flex;flex-direction:column;gap:8px;max-height:62vh;overflow-y:auto;
  padding:6px 2px 12px;border-radius:var(--r)}
.msg{position:relative;max-width:78%;align-self:flex-start;background:var(--panel-2);
  border:1px solid var(--line);border-radius:14px;padding:9px 13px}
.msg.mine{align-self:flex-end;background:rgba(124,58,237,.20);border-color:var(--line-2)}
.msg-body{font-size:14px;color:var(--read);line-height:1.5;white-space:pre-wrap;word-break:break-word}
.msg-at{font-size:10.5px;color:var(--muted-2);margin-top:3px;text-align:right}
/* The composer is a row — attachment, box, send — with `flex-wrap` so that
   the strip saying what this send is carrying can claim a whole line of its
   own above it. Without the wrap the strip is just another item in the row and
   sits BESIDE the box, splitting the composer in two. */
.chat-composer{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-top:14px}
.chat-composer > .chat-carry{flex:0 0 100%;margin-bottom:0}
.chat-composer .textarea{flex:1;resize:vertical;min-height:44px}
/* Message attachments: a picture, a video, or an audio clip inside the bubble. */
.msg-media{display:block;margin:0 0 6px;max-width:100%}
.msg-media img{display:block;max-width:100%;max-height:340px;width:auto;height:auto;border-radius:10px}
video.msg-media{display:block;max-width:min(340px,100%);max-height:340px;border-radius:10px;background:#000}
.msg-audio{display:block;width:min(280px,64vw);margin:2px 0 6px}
.msg-foot{display:flex;align-items:center;gap:8px;margin-top:3px;justify-content:flex-end}
.msg-at{font-size:10.5px;color:var(--muted-2)}
/* The flag button: quiet until the bubble is hovered, then it reads as a warning. */
.msg-report{margin:0;line-height:1}
.msg-report button{background:none;border:0;padding:0 2px;cursor:pointer;line-height:1;
  font-size:12px;color:var(--muted-2);opacity:.5;transition:.15s}
.msg:hover .msg-report button{opacity:.85}
.msg-report button:hover{color:var(--danger);opacity:1}
/* Taking your own message back. Hidden until the bubble is hovered -- it is a
   destructive control, so it should not be the first thing the eye lands on. */
.msg-act{margin:0;line-height:1}
.msg-act button{background:none;border:0;padding:0 2px;cursor:pointer;line-height:1;
  font-size:12px;color:var(--muted-2);opacity:0;transition:.15s}
.msg:hover .msg-act button{opacity:.7}
.msg-act button:hover{color:var(--danger);opacity:1}
/* No hover on a phone, so it has to be visible to be reachable at all. */
@media(hover:none){.msg-act button{opacity:.45}}
/* The tombstone left behind: it holds the message's place in the thread and
   says nothing else, so a removed message can still be seen to have existed. */
.msg.gone{background:transparent;border-style:dashed;box-shadow:none}
.msg-gone,.convo-gone{color:var(--muted-2);font-style:italic}
/* Dropping a conversation: beside the header, never inside it. */
.chat-headrow{display:flex;align-items:center;gap:6px;margin-bottom:16px}
.chat-headrow .chat-head,.chat-headrow .chat-head-link{flex:1;min-width:0;margin-bottom:0}
.chat-headact{margin:0;flex:0 0 auto}
.chat-headact button{background:none;border:0;padding:7px 9px;border-radius:9px;cursor:pointer;
  line-height:1;font-size:15px;color:var(--muted-2);opacity:.45;transition:.15s}
.chat-headact button:hover{opacity:1;color:var(--danger);background:rgba(255,255,255,.05)}
/* A one-off "reported" acknowledgement — green, distinct from the red error. */
.chat-flash{background:rgba(46,160,67,.16);border:1px solid rgba(46,160,67,.5);color:var(--read);
  border-radius:var(--r);padding:9px 13px;font-size:13.5px;margin-bottom:14px}
/* Composer: the paperclip, and the chosen-file chip above the box. */
.chat-attach{flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  width:42px;height:44px;border:1px solid var(--line);border-radius:12px;cursor:pointer;
  font-size:18px;background:var(--field);transition:.15s}
.chat-attach:hover{border-color:var(--violet)}
/* The emoji button is the paperclip's twin — same size, same box — because they
   are the same kind of thing: something that goes into the message. It is a
   <button> rather than a <label>, so it needs the cursor and the font reset a
   label got for free. */
.chat-emoji{font:inherit;font-size:18px;line-height:1;color:var(--muted);cursor:pointer}
.chat-emoji[hidden]{display:none}
.chat-composer-main{flex:1;display:flex;flex-direction:column;gap:6px;min-width:0}
.chat-attach-name{font-size:12px;color:var(--violet);background:var(--panel);
  border:1px solid var(--line);border-radius:8px;padding:3px 9px;align-self:flex-start;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
/* The conversation-list preview when the last message was a bare file. */
.convo-att{font-style:italic}
/* Admin: the message-report moderation queue. */
.rep{border-top:1px solid var(--line);padding:14px 2px}
.rep-meta{font-size:12.5px;margin-bottom:6px}
.rep-meta .when{color:var(--muted-2)}
.rep-reason{font-size:13px;color:var(--read);margin:0 0 8px}
.rep-msg{background:var(--panel-2);border:1px solid var(--line);border-radius:12px;padding:10px 12px;margin-bottom:10px}
.rep-body{font-size:14px;color:var(--read);line-height:1.5;white-space:pre-wrap;word-break:break-word;margin:0}
.rep-media{display:block;max-width:100%;margin:0 0 6px}
.rep-media img,video.rep-media{display:block;max-width:280px;max-height:220px;border-radius:8px}
.rep-msg audio{width:min(260px,60vw)}
.rep-acts{display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap}
/* Each report action beyond Dismiss is a <details>: its summary is a button,
   and opening it reveals the form (edit box, warn reason, ban duration). */
.rep-tool{position:relative}
.rep-tool>summary{list-style:none;cursor:pointer;display:inline-block}
.rep-tool>form{margin-top:8px;display:flex;flex-direction:column;gap:8px;max-width:420px}
.rep-tool>form .input,.rep-tool>form .textarea{width:100%}
.rep-tool>form .btn{align-self:flex-start}
.rep-tool[open]{flex-basis:100%}
/* Legacy alias kept so the delete tool needs no markup change. */
.rep-del>summary{list-style:none;cursor:pointer;display:inline-block}
/* "edited by a moderator" note in a message's footer. */
.msg-edited{font-size:10.5px;color:var(--muted-2);font-style:italic}
/* Users tab: ban state + control on an account row. */
.ubanned{font-size:12px;color:var(--danger);font-weight:600;align-self:center}
.uban{align-self:center}
/* Wallet + add-funds on a user row. */
.ubal{font-size:12.5px;color:var(--muted-2);align-self:center}
.ubal b{color:var(--gold)}
.ufunds{align-self:center}
.ufunds>form{flex-direction:row;flex-wrap:wrap;align-items:center;max-width:340px}
.ufunds .input{width:auto;flex:1;min-width:100px}
.uban>form{margin-top:8px;display:flex;flex-direction:column;gap:8px;max-width:320px}
.uban>form .btn{align-self:flex-start}
/* Moderation warning banner shown to a warned member atop their pages. */
.warnbar{background:rgba(248,113,113,.14);border:1px solid rgba(248,113,113,.55);
  border-radius:var(--r);margin:0 0 18px;padding:12px 15px}
.warnbar-in{display:flex;gap:14px;align-items:flex-start;flex-wrap:wrap}
.warnbar-body{flex:1;min-width:200px}
.warnbar-title{display:block;color:var(--danger);font-size:14px;margin-bottom:5px}
.warnbar-list{list-style:none;margin:0;padding:0;font-size:13.5px;color:var(--read);
  display:flex;flex-direction:column;gap:4px}
.warnbar-when{color:var(--muted-2);font-size:12px}
.warnbar-ack{flex:0 0 auto;align-self:center}
/* ---- Money: wallet widget, master pay form, admin panel, charts ---- */
.pay-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.pay-row .input{width:auto;flex:1;min-width:120px;max-width:220px}
.mhead-pay{margin:0 0 14px}
.wallet-tiles{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.wtile{flex:1;min-width:140px;background:var(--panel-2);border:1px solid var(--line);
  border-radius:var(--r);padding:12px 14px}
.wtile.on{border-color:var(--violet);background:rgba(124,58,237,.12)}
.wtile-k{display:block;font-size:12px;color:var(--muted-2);margin-bottom:4px}
.wtile-v{font-size:22px;font-weight:700;color:var(--gold)}
.wtile.on .wtile-v{color:var(--violet)}
.wallet-topup{margin:0 0 14px;max-width:none}
.wallet-escrows h3,.wallet-history h3{font-size:14px;margin:14px 0 8px}
.wescrow,.wdep{display:flex;align-items:center;gap:10px;padding:8px 0;border-top:1px solid var(--line);flex-wrap:wrap}
.wescrow-main{flex:1;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
.wescrow-acts{display:flex;gap:8px}
.wtag{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--warn,#eab308);
  border:1px solid currentColor;border-radius:6px;padding:1px 6px}
/* Admin money panel */
.mtiles{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:18px}
.mtile{flex:1;min-width:130px;background:var(--panel-2);border:1px solid var(--line);
  border-radius:var(--r);padding:12px 14px}
.mtile.big{min-width:180px;border-color:var(--violet);background:rgba(124,58,237,.12)}
.mtile.warn{border-color:var(--warn,#eab308)}
.mtile-k{display:block;font-size:12px;color:var(--muted-2);margin-bottom:5px}
.mtile-v{font-size:20px;font-weight:700}
.mtile.big .mtile-v{color:var(--violet);font-size:24px}
.mcharts{display:flex;gap:18px;flex-wrap:wrap;margin-bottom:20px}
.mchart{flex:1;min-width:280px;background:var(--panel-2);border:1px solid var(--line);
  border-radius:var(--r);padding:12px 14px}
.mchart-h{font-size:13px;font-weight:600;margin-bottom:8px}
.chart{display:block;width:100%;height:auto;overflow:visible}
.chart-bar{fill:var(--violet);opacity:.85}
.chart-area{fill:var(--violet);opacity:.14}
.chart-line{fill:none;stroke:var(--violet);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.chart-label{fill:var(--muted-2);font-size:10px}
.mcredit{background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);
  padding:12px 14px;margin-bottom:18px;max-width:520px}
.mcredit h3{font-size:14px;margin-bottom:10px}
.mcredit-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.mcredit-row .input{width:auto;flex:1;min-width:110px}
.mlist{margin-bottom:18px}
.mlist h3{font-size:14px;margin:0 0 8px}
.mrow{display:flex;align-items:center;gap:10px;padding:9px 0;border-top:1px solid var(--line);flex-wrap:wrap}
.mrow>b{min-width:90px}
.mrow-acts{margin-left:auto;display:flex;gap:8px}
.convo-list{display:flex;flex-direction:column;gap:2px;margin-top:10px}
.convo{display:flex;align-items:center;gap:13px;padding:12px 10px;border-radius:var(--r);
  text-decoration:none;color:inherit;border-top:1px solid var(--line);position:relative}
.convo:hover{background:var(--panel)}
.convo .av{width:46px;height:46px;flex:0 0 auto}
.convo-main{flex:1;min-width:0}
.convo-top{display:flex;align-items:baseline;gap:10px}
.convo-top b{font-size:14.5px}
.convo-top .when{margin-left:auto;font-size:11.5px;color:var(--muted-2)}
.convo-prev{font-size:13px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.convo.unread .convo-prev{color:var(--read)}
.convo.unread b{font-weight:700}
.convo-dot{width:9px;height:9px;border-radius:50%;background:var(--violet);flex:0 0 auto}
.offer-link{color:inherit;text-decoration:none}
.offer-link:hover{color:var(--violet)}
/* Unread badge on the Conversations tab. */
.navbadge{display:inline-block;min-width:16px;padding:0 4px;margin-left:3px;position:relative;top:-2px;
  font-size:9.5px;font-weight:700;line-height:16px;text-align:center;vertical-align:top;
  color:#fff;background:var(--violet-d);border-radius:8px}
.mhead-cta{margin:2px 0 20px}
.umsg{margin-left:auto}
.umsg .btn{white-space:nowrap}

*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  font-family:var(--sans);
  background:var(--bg);
  color:var(--text);
  line-height:1.6;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit}
img{max-width:100%}

/* ---- cosmic backdrop -----------------------------------------
   .bg-stars  : full starfield + nebula blooms (marketing pages)
   .bg-calm   : muted version for reading / app chrome
   -------------------------------------------------------------- */
.bg-stars::before,.bg-calm::before{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
  background:
    radial-gradient(1.5px 1.5px at 12% 18%,#fff,transparent),
    radial-gradient(1px 1px at 28% 62%,#DCD5FF,transparent),
    radial-gradient(1.5px 1.5px at 47% 12%,#fff,transparent),
    radial-gradient(1px 1px at 63% 44%,#C4B5FD,transparent),
    radial-gradient(2px 2px at 76% 22%,#fff,transparent),
    radial-gradient(1px 1px at 88% 70%,#DCD5FF,transparent),
    radial-gradient(1px 1px at 8% 84%,#fff,transparent),
    radial-gradient(1.5px 1.5px at 39% 88%,#C4B5FD,transparent),
    radial-gradient(1px 1px at 92% 36%,#fff,transparent),
    radial-gradient(1px 1px at 55% 76%,#DCD5FF,transparent);
}
.bg-stars::before{opacity:.55}
.bg-calm::before{opacity:.22}

.bg-stars::after,.bg-calm::after{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
}
.bg-stars::after{
  background:
    radial-gradient(60vw 50vh at 78% -8%,rgba(124,58,237,.30),transparent 70%),
    radial-gradient(50vw 40vh at 12% 22%,rgba(56,44,168,.28),transparent 70%),
    radial-gradient(45vw 40vh at 55% 108%,rgba(245,197,66,.10),transparent 70%);
}
.bg-calm::after{
  background:
    radial-gradient(70vw 46vh at 84% -14%,rgba(124,58,237,.16),transparent 72%),
    radial-gradient(52vw 34vh at 6% 8%,rgba(56,44,168,.13),transparent 72%);
}

/* ---- layout ---- */
.wrap{position:relative;z-index:1;max-width:var(--wrap);margin:0 auto;padding:0 24px}
.wrap-narrow{position:relative;z-index:1;max-width:760px;margin:0 auto;padding:0 24px}
.stack{position:relative;z-index:1}

/* ---- nav ---- */
.nav{position:sticky;top:0;z-index:40;backdrop-filter:blur(14px);
  background:rgba(10,5,24,.78);border-bottom:1px solid var(--line)}
.nav-in{max-width:var(--wrap);margin:0 auto;padding:15px 24px;display:flex;align-items:center;gap:30px}
.logo{font-size:20px;font-weight:600;letter-spacing:.5px;display:flex;align-items:center;gap:9px;flex:0 0 auto}
.logo b{background:linear-gradient(100deg,var(--gold),var(--violet));
  -webkit-background-clip:text;background-clip:text;color:transparent;font-weight:600}
.nav-links{display:flex;gap:26px;margin-left:auto;font-size:14.5px}
.nav-links a{color:var(--muted);transition:color .2s}
.nav-links a:hover,.nav-links a.on{color:var(--text)}

/* ---- buttons ---- */
.btn{display:inline-block;border:0;cursor:pointer;text-align:center;
  border-radius:100px;padding:11px 24px;font-size:14.5px;font-weight:600;transition:.22s}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn-p{background:linear-gradient(100deg,var(--violet-d),#9333EA);color:#fff;box-shadow:var(--glow)}
.btn-p:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 10px 34px rgba(124,58,237,.6)}
.btn-g{background:transparent;color:var(--text);border:1px solid var(--line)}
.btn-g:hover:not(:disabled){background:var(--panel);border-color:var(--violet)}
/* Danger — a destructive action, outlined rather than filled so it reads as a
   deliberate choice and not the default one. Taking a message down wears this. */
.btn-d{background:transparent;color:var(--danger);border:1px solid var(--danger)}
.btn-d:hover:not(:disabled){background:var(--danger);color:#fff}
.btn-gold{background:linear-gradient(100deg,var(--gold-d),var(--gold));color:#2A1A00;
  box-shadow:0 6px 26px rgba(245,197,66,.35)}
.btn-gold:hover:not(:disabled){transform:translateY(-2px)}
.btn-sm{padding:8px 17px;font-size:13.2px}
.btn-lg{padding:14px 32px;font-size:16px}
.btn-block{display:block;width:100%}

/* ---- panels ---- */
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:22px;backdrop-filter:blur(8px)}
.panel-solid{background:var(--panel-solid);border:1px solid var(--line);border-radius:var(--r-lg);padding:22px}
.panel-hover{transition:.24s}
.panel-hover:hover{transform:translateY(-4px);border-color:var(--line-2);
  box-shadow:0 16px 40px rgba(124,58,237,.22)}

/* ---- badges / pills ---- */
.pill{display:inline-flex;align-items:center;gap:8px;padding:7px 16px;border-radius:100px;
  background:var(--panel);border:1px solid var(--line);font-size:13px;color:var(--violet)}
.badge{display:inline-block;border:1px solid var(--line);border-radius:6px;padding:2px 8px;
  font-size:11px;color:var(--violet)}
.chip{display:inline-block;background:var(--panel);border:1px solid var(--line);
  border-radius:100px;padding:6px 14px;font-size:13px;color:var(--muted)}
.dot{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);flex:0 0 7px}
.dot.off{background:#6B7280;box-shadow:none}
.status{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--ok)}
.status.off{color:var(--muted)}

/* ---- avatar ---- */
.av{border-radius:50%;display:grid;place-items:center;position:relative;flex:0 0 auto;
  background:linear-gradient(140deg,#4C2E8F,#7C3AED);border:2px solid rgba(245,197,66,.42)}
.av-sm{width:40px;height:40px;font-size:17px}
.av-md{width:54px;height:54px;font-size:22px}
.av-lg{width:92px;height:92px;font-size:38px}
.av .s{position:absolute;bottom:1px;right:1px;width:13px;height:13px;border-radius:50%;
  background:var(--ok);border:2.5px solid var(--bg)}
.av .s.off{background:#6B7280}

/* ---- forms ---- */
.input,.textarea{width:100%;background:var(--field);border:1px solid var(--field-line);
  border-radius:var(--r);padding:12px 15px;color:var(--text);font:inherit;font-size:14.5px;
  transition:.18s;resize:none}
.input::placeholder,.textarea::placeholder{color:var(--muted-2)}
.input:focus,.textarea:focus{outline:0;border-color:var(--violet);
  box-shadow:0 0 0 3px rgba(124,58,237,.2)}
.label{display:block;font-size:12.5px;color:var(--muted);margin-bottom:7px;
  letter-spacing:.4px;text-transform:uppercase}

/* ---- headings ---- */
.h1{font-size:clamp(34px,5.4vw,56px);line-height:1.1;font-weight:700;letter-spacing:-1.4px}
.h2{font-size:clamp(26px,3.8vw,38px);font-weight:700;letter-spacing:-.8px}
.h3{font-size:21px;font-weight:600;letter-spacing:-.3px}
.grad{background:linear-gradient(100deg,var(--gold),var(--violet) 60%,#F0ABFC);
  -webkit-background-clip:text;background-clip:text;color:transparent;font-style:normal}
.sec{padding:72px 0}
.sec-sm{padding:44px 0}
.sec-h{text-align:center;margin-bottom:44px}
.sec-h p{color:var(--muted);font-size:16.5px;margin-top:11px}

/* ---- long-form reading surface (SEO/content pages) ---- */
.prose{color:var(--read);font-size:16.5px;line-height:1.82;max-width:68ch}
.prose p{margin-bottom:18px}
.prose h2{font-size:26px;font-weight:700;letter-spacing:-.5px;margin:38px 0 14px;color:var(--text)}
.prose h3{font-size:19px;font-weight:600;margin:28px 0 10px;color:var(--text)}
.prose ul,.prose ol{margin:0 0 18px 22px}
.prose li{margin-bottom:8px}
.prose strong{color:var(--text);font-weight:600}
.prose em{color:var(--violet);font-style:italic}
.prose a{color:var(--violet);border-bottom:1px solid var(--line-2)}
.prose a:hover{color:var(--gold);border-color:var(--gold)}
.prose blockquote{border-left:2px solid var(--gold);padding:4px 0 4px 20px;margin:24px 0;
  color:var(--muted);font-style:italic}

/* ---- breadcrumbs ---- */
.crumbs{display:flex;gap:9px;flex-wrap:wrap;font-size:13px;color:var(--muted-2);margin-bottom:22px}
.crumbs a{color:var(--muted)}
.crumbs a:hover{color:var(--violet)}

/* ---- tarot card ---- */
.tcard{border-radius:var(--r-md);display:flex;flex-direction:column;align-items:center;
  justify-content:center;padding:18px;text-align:center;position:relative}
.tcard-back{background:linear-gradient(158deg,#2B1A5E,#170C3A);
  border:1px solid rgba(245,197,66,.34);
  box-shadow:var(--shadow),inset 0 0 40px rgba(124,58,237,.28)}
.tcard-back::after{content:"";position:absolute;inset:9px;
  border:1px solid rgba(245,197,66,.22);border-radius:10px}
.tcard-front{background:linear-gradient(158deg,#3B2678,#1E1147);
  border:1px solid rgba(245,197,66,.5);box-shadow:0 14px 44px rgba(124,58,237,.45)}

/* ---- table ---- */
.tbl{width:100%;border-collapse:collapse;font-size:14.5px}
.tbl th{text-align:left;font-size:12px;letter-spacing:1px;text-transform:uppercase;
  color:var(--muted);font-weight:600;padding:11px 14px;border-bottom:1px solid var(--line)}
.tbl td{padding:13px 14px;border-bottom:1px solid var(--line);color:var(--read)}
.tbl tr:last-child td{border-bottom:0}

/* ---- footer ---- */
.foot{border-top:1px solid var(--line);padding:40px 0 48px;margin-top:40px;position:relative;z-index:1}
.foot-links{display:flex;gap:10px 24px;flex-wrap:wrap;justify-content:center;
  font-size:13.5px;color:var(--muted);margin:18px 0 24px}
.foot-links a:hover{color:var(--text)}
.disc{font-size:12.5px;color:var(--muted);max-width:640px;margin:0 auto;line-height:1.7;text-align:center}

/* ---- reactions on a message ---- */
/* The same idea as a post's, smaller: a chat bubble has less room and the
   chips sit under the words rather than beside them. */
.mreacts{display:flex;flex-wrap:wrap;gap:4px;align-items:center;margin-top:6px}
.mreacts form{margin:0}
.mreact{display:inline-flex;align-items:center;gap:4px;font-size:12px;
  padding:2px 8px;border-radius:100px;border:1px solid var(--line);
  background:var(--panel-2);color:var(--muted);cursor:pointer;line-height:1.6}
.mreact:hover{border-color:var(--violet);color:var(--text)}
.mreact.on{border-color:var(--violet-d);background:rgba(124,58,237,.18);color:var(--text)}
.mreact-add{padding:2px 9px;color:var(--muted-2)}
/* The controls for LEAVING a reaction sit in the margin beside the bubble, on
   the side the bubble is NOT anchored to — your own messages hug the right, so
   theirs go left; everybody else's hug the left, so theirs go right. That half
   of the line is empty by construction (a bubble is at most 78% wide), which is
   both why there is room and why it is the right place: under the bubble they
   cost every message a blank row whether or not anybody ever reacts.

   Out of flow, so a message with no reactions is exactly as tall as its words.
   `.mreact-zone` stays in flow but has nothing in it to give it height until
   somebody actually leaves something. */
.mreact-side{position:absolute;bottom:0;display:flex;align-items:flex-end;gap:4px}
.msg:not(.mine) .mreact-side{left:100%;margin-left:6px;flex-direction:row}
.msg.mine .mreact-side{right:100%;margin-right:6px;flex-direction:row-reverse}
.mreact-side form{margin:0}


/* ---- a message that carries another one ---- */
/* A reply quotes; a forward names whose words these were. Both are the
   original sender's text and neither is editable — see migration 0056. */
.msg-quote{display:flex;flex-direction:column;gap:2px;margin-bottom:6px;
  padding:6px 10px;border-left:3px solid var(--violet-d);border-radius:0 8px 8px 0;
  background:rgba(255,255,255,.04);font-size:12.5px}
.msg-quote.fwd{border-left-color:var(--gold)}
.msg-quote-h{color:var(--muted-2);font-size:11.5px}
.msg-quote-h a{color:var(--violet);font-weight:600}
.msg-quote-b{color:var(--muted);line-height:1.5;overflow-wrap:anywhere}
.msg-quote-f{color:var(--muted-2);font-size:11.5px}
.msg-quote-who{display:inline-block;font-size:12.5px;font-weight:600;
  color:var(--violet);margin-bottom:4px}
/* The strip above the composer saying what this send is carrying. */
.chat-carry{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:8px 12px;margin-bottom:8px;border-radius:10px;
  border:1px solid var(--line);border-left:3px solid var(--violet-d);
  background:var(--panel);font-size:12.5px}
.chat-carry.fwd{border-left-color:var(--gold)}
.chat-carry-h{font-weight:600;color:var(--muted-2)}
.chat-carry-b{color:var(--muted);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;min-width:0;flex:1}
.chat-carry-x{color:var(--muted-2);font-size:14px;flex:0 0 auto}
.chat-carry-x:hover{color:var(--danger)}

/* ---- the emoji picker ---- */
/* One panel per page, `position: fixed`, centred.

   Fixed and not absolute, and centred rather than anchored to whatever was
   pressed, because the trigger sits INSIDE the thread — and the thread is a
   scroll container, so it clips. The old per-message picker hung off a control
   in the right-hand margin and the panel ran off the left edge of the screen at
   narrow widths; there is no anchoring rule that survives thirteen hundred
   symbols next to a control that could be anywhere on the line.

   `max-width` in `min()` with a viewport unit so it is a panel on a desktop and
   very nearly the whole screen on a phone, without a media query. */
.epick{position:fixed;z-index:120;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(420px,94vw);max-height:min(70vh,560px);display:flex;flex-direction:column;
  border-radius:16px;border:1px solid var(--line-2);background:var(--panel-solid);
  box-shadow:0 24px 60px rgba(0,0,0,.45)}
.epick[hidden]{display:none}
.epick form{display:flex;flex-direction:column;min-height:0;margin:0}
/* Nothing scrolls behind an open panel: a page moving under a modal is a page
   you have to find your place in again afterwards. */
body.epick-on{overflow:hidden}

.epick-top{display:flex;gap:8px;align-items:center;padding:12px 12px 8px;
  border-bottom:1px solid var(--line)}
.epick-find{flex:1;min-width:0;font:inherit;font-size:14px;padding:8px 12px;
  border-radius:10px;border:1px solid var(--field-line);background:var(--field);
  color:var(--text)}
.epick-find:focus{outline:2px solid var(--violet);outline-offset:1px}
.epick-x{flex:0 0 auto;border:0;background:none;cursor:pointer;font-size:16px;
  line-height:1;padding:8px;color:var(--muted-2);border-radius:8px}
.epick-x:hover{color:var(--text);background:var(--panel-2)}

.epick-body{flex:1;min-height:0;overflow-y:auto;padding:6px 12px 14px}
.epick-sect[hidden]{display:none}
.epick-h{position:sticky;top:0;z-index:1;margin:8px 0 4px;padding:4px 0;
  font-size:11.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted-2);background:var(--panel-solid)}
/* `auto-fill` rather than a fixed count: the same grid works at 420px and at
   the width a phone leaves. */
.epick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(38px,1fr));gap:2px}
.epick-grid button{border:0;background:none;cursor:pointer;border-radius:8px;
  padding:4px 0;font-size:22px;line-height:1.3;transition:background .1s}
.epick-grid button[hidden]{display:none}
.epick-grid button:hover{background:var(--panel-2)}
.epick-grid button:focus-visible{outline:2px solid var(--violet);outline-offset:-2px}
.epick-none{margin:18px 0;text-align:center;color:var(--muted-2);font-size:13px}

/* Opened from the composer, it is a popover over the message box rather than a
   modal in the middle of the screen. Where it goes is set in emoji.js — only the
   browser knows where the composer landed — so the rule here only turns the
   shadow the other way up, to lift the panel off the input beneath it. */
.epick-compose{box-shadow:0 -10px 44px rgba(0,0,0,.5)}

/* On its own page it is not a panel at all — it is the content. */
.epick-page{position:static;transform:none;width:auto;max-width:none;
  max-height:none;box-shadow:none;margin-top:14px}
.epick-page .epick-body{max-height:none;overflow:visible}
.epick-page .epick-x{display:none}

/* ---- who comes here ---- */
/* The breakdowns beside the daily chart: country, language, kind, path. Each
   row is a name, a proportion, and the two numbers behind it. */
.vbreak{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;
  margin-top:22px}
.vpanel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:16px 18px}
.vpanel.wide{grid-column:1/-1}
.vpanel h3{font-size:15px;font-weight:700;margin-bottom:4px}
.vpanel .fhint{margin-bottom:12px}
.vrow{display:grid;grid-template-columns:minmax(0,1fr) 90px auto;gap:10px;
  align-items:center;padding:4px 0;font-size:13.5px}
.vname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vbar{height:6px;border-radius:100px;background:var(--panel-2);overflow:hidden}
.vbar i{display:block;height:100%;background:var(--violet-d);border-radius:100px}
.vnum{font-variant-numeric:tabular-nums;font-size:12.5px;white-space:nowrap}
/* The uniques bar sits inside the views bar, so it needs to read as the same
   quantity seen closer up rather than as a second one. */
.chart-bar-u{fill:var(--gold);opacity:.85}
.mchart.wide{grid-column:1/-1}
@media (max-width:520px){
  .vrow{grid-template-columns:minmax(0,1fr) auto}
  .vbar{display:none}
}

/* ---- paid membership ---- */
/* What a reader sells access to, as a visitor sees it. */
.tier-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px}
.tier-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:20px 20px 16px;display:flex;flex-direction:column;gap:9px}
.tier-card.on{border-color:var(--violet-d)}
.tier-card h3{font-size:17px;font-weight:700;letter-spacing:-.2px}
/* The cover, edge to edge across the top of the card. Fixed height and
   object-fit, because a tier's picture is whatever shape somebody uploaded and
   a ladder of three cards with three different heights is not a ladder. */
.tier-cover{margin:-20px -20px 4px;height:130px;overflow:hidden;
  border-radius:var(--r-lg) var(--r-lg) 0 0}
.tier-cover img{width:100%;height:100%;object-fit:cover;display:block}
/* The same picture in the editor, smaller, beside the form that replaces it. */
.tier-upload{display:flex;align-items:flex-start;gap:14px;flex-wrap:wrap;
  margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
/* `flex:0 0` and not just a width: as a flex item it would otherwise shrink,
   and an image the browser has not loaded yet has an intrinsic width of zero —
   which collapses the thumbnail to a sliver until it arrives. */
.tier-upload img{flex:0 0 120px;width:120px;height:72px;object-fit:cover;border-radius:9px;
  border:1px solid var(--line)}
.tier-upload-acts{display:flex;align-items:flex-end;gap:8px;flex-wrap:wrap;flex:1 1 240px}
.tier-upload-acts .mini{flex:1 1 180px}
.tier-price b{font-size:22px;font-weight:700;color:var(--gold)}
.tier-price span{font-size:12.5px;color:var(--muted-2);margin-left:4px}
.tier-desc{font-size:13.8px;color:var(--muted);line-height:1.6}
.tier-foot{margin-top:auto;padding-top:10px;display:flex;align-items:center;gap:10px}
.tier-foot form{margin:0}
.tier-count{font-size:12.5px;color:var(--muted-2);align-self:center}
.tier-mine{font-size:12.5px;color:var(--ok)}
.tier-lapsed{font-size:12.5px;color:var(--muted-2)}
/* The editor's on/off switch sits in the same row as a price. */
.tier-on{flex-direction:row;align-items:center;gap:7px}

/* A post somebody has not paid for. The body is not in the HTML at all — this
   is the offer that stands where it was, not a blur over it, because a blur is
   a thing you can remove with a stylesheet. */
.wlock{border:1px dashed var(--line-2);border-radius:var(--r-lg);
  background:rgba(124,58,237,.06);padding:20px;text-align:center;
  display:flex;flex-direction:column;align-items:center;gap:8px}
.wlock-h{font-size:14.5px;font-weight:600}
.wlock-b{font-size:13.5px;color:var(--muted);max-width:420px;line-height:1.6}
.wlock .btn{margin-top:4px}

/* The composer's "visible to" picker, only there for somebody who sells. */
.wvis{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px;
  font-size:12.5px;color:var(--muted-2)}
.wvis-h{font-weight:600}
.wvis-t{display:inline-flex;align-items:center;gap:6px;cursor:pointer;
  border:1px solid var(--line);border-radius:100px;padding:4px 11px}
.wvis-t:hover{border-color:var(--violet)}
.wvis-t:has(input:checked){border-color:var(--violet-d);background:rgba(124,58,237,.14);color:var(--text)}
.wvis-hint{flex:1 1 100%;font-size:12px;line-height:1.5}

/* Choosing a language.
   There used to be a segmented RU/EN control in every header. It is a setting
   now — it sits with the theme in /lk, and the front page names both languages
   in full at the very bottom, which is the one place a person who cannot read
   the site can still find it. */
/* ---- a master's own service editor ---- */
.sect h2 .cnt{font-size:13px;font-weight:400;color:var(--muted-2);margin-left:8px;
  font-variant-numeric:tabular-nums}
.svclist{display:flex;flex-direction:column;gap:12px;margin-bottom:20px}
.svc{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:16px 18px;display:flex;flex-direction:column;gap:10px;backdrop-filter:blur(8px)}
/* Hidden services stay legible but visibly not live. */
.svc.off{opacity:.62;border-style:dashed}
.svc-new{border-color:var(--line-2);background:rgba(124,58,237,.07)}
.svc-new h3{font-size:15px;margin-bottom:2px}
.svc-row{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap}
.svc-row > .input{flex:1;min-width:220px}
.svc-ord{display:flex;gap:4px;flex:0 0 auto}
.iconbtn{width:34px;height:34px;border-radius:var(--r);border:1px solid var(--line);
  background:rgba(0,0,0,.2);color:var(--muted);font-size:15px;line-height:1;cursor:pointer;
  transition:.15s}
.iconbtn:hover:not(:disabled){border-color:var(--violet);color:var(--text)}
.iconbtn:disabled{opacity:.3;cursor:default}
.mini{display:flex;flex-direction:column;gap:5px;flex:1;min-width:130px}
.mini > span{font-size:11.5px;letter-spacing:.7px;text-transform:uppercase;color:var(--muted-2)}
.svc-acts{display:flex;gap:10px;align-items:center;flex-wrap:wrap;
  padding-top:10px;border-top:1px solid var(--line)}
.svc-acts .btn.danger{margin-left:auto;color:var(--danger);border-color:rgba(248,113,113,.3)}
.svc-acts .btn.danger:hover{border-color:var(--danger);background:rgba(248,113,113,.1)}

/* ---- service photos ---- */
.svc-photos{padding-top:12px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:10px}
.ph-head{display:flex;align-items:baseline;gap:8px;font-size:11.5px;letter-spacing:.7px;
  text-transform:uppercase;color:var(--muted-2)}
.ph-head .cnt{margin-left:auto;font-variant-numeric:tabular-nums}
.ph-strip{display:flex;gap:8px;flex-wrap:wrap}
.ph{position:relative;line-height:0}
.ph img{width:120px;height:90px;object-fit:cover;border-radius:var(--r);
  border:1px solid var(--line);background:#0E0722}
/* A video thumbnail carries its own controls, so it is fit whole rather than
   cropped — a cover-crop hides the scrubber the master needs to check it. */
.ph video{width:120px;height:90px;object-fit:contain;border-radius:var(--r);
  border:1px solid var(--line);background:#000}
/* The delete control sits on the thumbnail; its form must not add layout. */
.ph form{position:absolute;top:4px;right:4px;line-height:1}
.ph-x{width:22px;height:22px;border-radius:50%;border:0;cursor:pointer;font-size:11px;
  background:rgba(10,4,26,.82);color:var(--danger);display:grid;place-items:center;
  opacity:0;transition:.15s}
.ph:hover .ph-x,.ph-x:focus{opacity:1}
.ph-x:hover{background:var(--danger);color:#fff}
.ph-add{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.ph-add .input{flex:1;min-width:220px;padding:8px 10px;font-size:13px}
.ph-add .input::file-selector-button{background:var(--panel-2);color:var(--text);
  border:1px solid var(--line);border-radius:var(--r);padding:5px 12px;margin-right:10px;
  font:inherit;font-size:12.5px;cursor:pointer}
.ph-add .input::file-selector-button:hover{border-color:var(--violet)}
/* Public: larger, and a link to the full image. */
.ph-strip.pub{margin:12px 0 4px}
.ph-strip.pub img{width:220px;height:165px;transition:.18s}
.ph-strip.pub a:hover img{border-color:var(--violet)}
/* A service video plays where it sits — its own controls, no lightbox. Fit
   whole on black so a portrait clip letterboxes instead of stretching. */
.ph-vid{width:220px;height:165px;max-width:100%;object-fit:contain;
  border-radius:var(--r);border:1px solid var(--line);background:#000}

/* ---- lightbox ---- */
/* Only the overlay scrolls while it is open. */
html.lb-open{overflow:hidden}
.lb{position:fixed;inset:0;z-index:200;display:none;
  background:rgba(6,2,16,.94);backdrop-filter:blur(6px);
  align-items:center;justify-content:center;padding:0}
/* The dark comes up under the card rather than snapping on with it. Short —
   this is the room going quiet, not the event. */
.lb.on{display:flex;animation:lb-dim .26s ease both}
@keyframes lb-dim{from{opacity:0}to{opacity:1}}
@media(prefers-reduced-motion:reduce){.lb.on{animation:none}}
/* figure carries a UA margin; without this the image is inset by 40px a side. */
.lb-stage{margin:0;display:flex;align-items:center;justify-content:center;
  max-width:100%;max-height:100%}
/* Only the bottom bar is reserved — the close button and the arrows overlay the
   picture rather than taking space from it, so this is as close to full-screen
   as it can be while leaving somewhere to put the counter. */
.lb-stage img{max-width:calc(100vw - 20px);max-height:calc(100vh - 72px);
  border-radius:var(--r-lg);box-shadow:0 24px 70px rgba(0,0,0,.7);
  /* The image is the one thing a tap must not dismiss. */
  cursor:default}
.lb-bar{position:absolute;bottom:0;left:0;right:0;min-height:52px;
  display:flex;gap:14px;align-items:center;justify-content:center;flex-wrap:wrap;
  padding:12px 60px;
  /* Keeps the caption legible over a light photo that reaches the bottom. */
  background:linear-gradient(to top,rgba(6,2,16,.85),transparent)}
.lb-cap{font-size:13.5px;color:var(--muted);text-align:center;max-width:56ch}
.lb-x{position:absolute;top:14px;right:16px;width:40px;height:40px;border-radius:50%;
  border:1px solid var(--line);background:rgba(10,4,26,.75);color:var(--muted);
  font-size:16px;cursor:pointer;transition:.15s}
.lb-x:hover{border-color:var(--violet);color:#fff}
.lb-nav{position:absolute;top:50%;transform:translateY(-50%);
  width:46px;height:70px;border-radius:var(--r-lg);border:1px solid var(--line);
  background:rgba(10,4,26,.6);color:var(--muted);font-size:30px;line-height:1;
  cursor:pointer;transition:.15s}
.lb-nav:hover{border-color:var(--violet);color:#fff;background:rgba(10,4,26,.9)}
.lb-nav[hidden]{display:none}
.lb-prev{left:14px}
.lb-next{right:14px}
.lb-dots{font-size:12.5px;letter-spacing:1.4px;color:var(--muted-2);
  font-variant-numeric:tabular-nums;white-space:nowrap}
/* On a phone the arrows eat the picture; swipe is the gesture there anyway. */
@media(max-width:640px){
  .lb-nav{display:none}
  .lb-bar{padding:10px 14px;min-height:44px}
  .lb-stage img{max-width:calc(100vw - 8px);max-height:calc(100vh - 60px)}
}

/* ---- a card, opened -------------------------------------------------------
   The photo viewer shows one thing. This one shows a card and what it says,
   and the card can be turned over — so the stage is a positioned box two
   slots deep: the one leaving and the one arriving are on screen together for
   the length of the move. */
.lbc-stage{position:relative;width:min(92vw,760px);height:calc(100vh - 108px)}

/* --- the flight -------------------------------------------------------------
   The slot carries the travel across the screen. Slower than a UI transition
   usually wants to be, because the point is to watch the card turn over on the
   way in — at 0.4s you see a smear and arrive. */
.lbc-slot{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:20px;
  transition:transform .78s cubic-bezier(.2,.62,.25,1),opacity .78s ease;
  transform:translateX(0);opacity:1;
  /* Depth, so a card turning reads as a sheet rotating in space rather than
     squashing flat. It has to sit HERE, on the card's own parent: perspective
     applies to an element's children, and from the stage the card is a
     grandchild — every rotation was rendering as an orthographic squash with
     no near edge and no far edge at all. */
  perspective:1400px}
.lbc-slot.from-right{transform:translateX(52vw);opacity:0}
.lbc-slot.from-left {transform:translateX(-52vw);opacity:0}
.lbc-slot.to-left   {transform:translateX(-52vw);opacity:0}
.lbc-slot.to-right  {transform:translateX(52vw);opacity:0}
/* Opening and closing: no travel, so the card turns over on the spot and the
   zoom is an arrival rather than a pop. */
/* Opening no longer uses a class — the card is flown in from the shelf card
   you clicked, which needs measurements a stylesheet cannot take. */

/* --- the card ---------------------------------------------------------------
   Two faces on one sheet. The card is held at half a turn while it is entering
   — which shows the back — and released to zero, so it finishes turning over
   just as it reaches the middle. */
/* NO filter here, however tempting drop-shadow is. A filter other than none
   forces the used value of transform-style to flat, which quietly collapses
   the two faces into one plane: the card still rotates, but the back never
   comes up and you watch the front swing round mirrored. The shadow lives on
   each face as a box-shadow instead, which is cheaper anyway. */
.lbc-card{width:min(58vw,300px);aspect-ratio:2/3;position:relative;
  transform-style:preserve-3d;transform:rotateY(0deg);
  transition:transform .78s cubic-bezier(.2,.62,.25,1);cursor:default}
.lbc-slot.in .lbc-card{transform:rotateY(180deg)}
/* Leaving, it turns the other way, so a card going left does not look like a
   card arriving from the left played backwards. */
.lbc-slot.to-left  .lbc-card{transform:rotateY(-180deg)}
.lbc-slot.to-right .lbc-card{transform:rotateY(180deg)}

.lbc-face{position:absolute;inset:0;border-radius:var(--r-lg);
  border:1px solid var(--line);overflow:hidden;
  background:linear-gradient(160deg,#170a2e,#0d0520);
  box-shadow:0 24px 50px rgba(0,0,0,.7);
  /* Without this you see the far side of the sheet through the near one. */
  backface-visibility:hidden;-webkit-backface-visibility:hidden}
.lbc-front{transform:rotateY(0deg)}
.lbc-back {transform:rotateY(180deg)}

/* The light that runs across the face as the card turns and as you move over
   it. Positioned from the pointer, and parked off to one side otherwise. */
.lbc-sheen{position:absolute;inset:0;pointer-events:none;mix-blend-mode:screen;
  opacity:.55;transition:opacity .3s;
  background:radial-gradient(120% 90% at var(--gx,26%) var(--gy,14%),
    rgba(255,255,255,.34),rgba(255,255,255,.06) 42%,transparent 68%)}

/* The burst behind the card as it lands. Its own element, because animating a
   box-shadow every frame is a repaint and animating opacity is not. */
.lbc-flare{position:absolute;left:50%;top:50%;width:min(78vw,420px);aspect-ratio:1;
  transform:translate(-50%,-50%) scale(.3);opacity:0;pointer-events:none;
  background:radial-gradient(circle,rgba(245,197,66,.5),rgba(167,139,250,.28) 38%,transparent 68%)}

/* Reversed turns the picture, not the card: a half-turn of the whole sheet
   would also turn the frame and, worse, would fight the entry rotation for
   the same transform. */
.lbc-inner{width:100%;height:100%;transition:transform .55s cubic-bezier(.34,1.28,.5,1)}
.lbc-stage.rev .lbc-inner{transform:rotate(180deg)}

.lbc-art{width:100%;height:100%;object-fit:cover;display:block}
.lbc-drawn{width:100%;height:100%;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:10px;text-align:center;padding:16px}
.lbc-drawn .sym{font-size:64px;line-height:1;color:var(--violet)}
.lbc-drawn .num{font-size:13px;letter-spacing:2px;color:var(--muted-2)}
.lbc-drawn .nm{font-size:15px;font-weight:600;color:#fff}
.lbc-drawn-back{background:
  radial-gradient(circle at 50% 42%,rgba(167,139,250,.16),transparent 62%),
  linear-gradient(160deg,#1b0d38,#0d0520)}

/* The words stay the right way up while the card turns. Reading upside-down
   text is not the point of the gesture. */
.lbc-meta{text-align:center;max-width:44ch;padding:0 12px}
.lbc-meta h3{margin:0 0 6px;font-size:19px;color:#fff}
.lbc-dupe{font-size:13px;color:var(--muted-2);font-weight:500}
.lbc-way{margin:0 0 6px;font-size:11.5px;letter-spacing:2px;text-transform:uppercase;
  color:var(--violet)}
.lbc-text{margin:0;font-size:14.5px;line-height:1.55;color:var(--muted)}
.lbc-said{animation:lbc-fade .32s ease}
@keyframes lbc-fade{from{opacity:0}to{opacity:1}}

.lb-turn{display:inline-flex;align-items:center;gap:7px;
  padding:7px 14px;border-radius:999px;border:1px solid var(--line);
  background:rgba(10,4,26,.75);color:var(--muted);font-size:13px;
  cursor:pointer;transition:.15s}
.lb-turn:hover{border-color:var(--violet);color:#fff}
.lb-turn[aria-pressed="true"]{border-color:var(--violet);color:#fff}
.lb-turn-i{font-size:15px;display:inline-block;transition:transform .5s}
.lb-turn[aria-pressed="true"] .lb-turn-i{transform:rotate(180deg)}

@media(max-width:640px){
  .lbc-stage{width:100vw;height:calc(100vh - 96px)}
  .lbc-card{width:min(70vw,260px)}
  .lbc-meta{max-width:34ch}
  .lbc-text{font-size:13.5px}
}
/* Somebody who has asked for less movement gets the change, not the journey.
   The script takes the same branch and skips the entry classes altogether, so
   this is belt and braces rather than the only guard. */
@media(prefers-reduced-motion:reduce){
  .lbc-slot,.lbc-card,.lbc-inner,.lb-turn-i{transition:none}
  .lbc-said{animation:none}
}

/* An owned card is a button now. Strip the UA chrome so it still looks like a
   card, and give it the affordance that it opens. */
.tc-have{padding:0;border:0;background:none;font:inherit;color:inherit;
  cursor:zoom-in;text-align:inherit}
.tc-have:focus-visible{outline:2px solid var(--violet);outline-offset:3px;border-radius:var(--r-lg)}
.ph-lb{display:block;line-height:0;cursor:zoom-in}

/* ---- a laid card, inspected -----------------------------------------------
   The spread's own cards opened one at a time: the card large, and under it the
   position it fell in and what that position says. No turn — a laid card is
   read the way it fell. */
/* Depth on the card's own parent, so the pointer tilt leans it in space with a
   near and a far edge rather than skewing it flat. */
.lbt-stage{flex-direction:column;gap:20px;width:min(94vw,560px);
  max-height:calc(100vh - 76px);padding:6px 0;perspective:1400px}
.lbt-card{height:min(52vh,460px);aspect-ratio:2/3;position:relative;flex:0 0 auto;
  border-radius:var(--r-lg);overflow:hidden;border:1px solid var(--line);
  background:#0E0722;box-shadow:0 24px 70px rgba(0,0,0,.7);
  /* The lean tracks the pointer with transition:none; this carries it back to
     flat when the pointer leaves. */
  transition:transform .3s cubic-bezier(.2,.7,.3,1);
  /* The card is the thing you came to look at; a tap on it must not dismiss. */
  cursor:default}
.lbt-in{width:100%;height:100%;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:12px}
/* Reversed if it fell reversed — the same rotation the felt uses, on the inner
   so the entrance zoom and the pointer lean can drive the card's own transform
   without a fight. */
.lbt-card.rev .lbt-in{transform:rotate(180deg)}
/* The light that tracks the pointer across the face, parked off to one corner
   otherwise — the same sheen the collection card has. Clipped to the card by
   its overflow, screen-blended so it lifts the art rather than washing it. */
.lbt-sheen{position:absolute;inset:0;pointer-events:none;mix-blend-mode:screen;
  opacity:.55;transition:opacity .3s;
  background:radial-gradient(120% 90% at var(--gx,26%) var(--gy,14%),
    rgba(255,255,255,.34),rgba(255,255,255,.06) 42%,transparent 68%)}
.lbt-in img{width:100%;height:100%;object-fit:cover;display:block}
.lbt-in .sym{font-size:96px;line-height:1;color:var(--violet)}
.lbt-in .num{font-size:15px;letter-spacing:3px;color:var(--muted-2)}
.lbt-in .nm{font-size:22px;font-weight:600;color:#fff;text-align:center;padding:0 14px}
.lbt-meta{max-width:52ch;text-align:center;cursor:default}
.lbt-slot{margin:0;font-size:16px;color:#fff}
.lbt-slot .lbt-n{color:var(--gold);font-weight:700;margin-right:4px}
.lbt-mean{margin:8px 0 0;font-size:14px;line-height:1.6;color:var(--muted)}
@media(max-width:640px){
  .lbt-stage{width:100vw}
  .lbt-card{height:min(44vh,360px)}
  .lbt-in .sym{font-size:72px}
  .lbt-in .nm{font-size:19px}
}
@media(prefers-reduced-motion:reduce){.lbt-card{transition:none}}

/* A laid card you can open. The affordance only appears once lightbox.js has
   upgraded the figure, so a no-JS spread doesn't dangle a dead cursor. */
.tbl-place.zoomable{cursor:zoom-in}
.tbl-place.zoomable:focus-visible{outline:2px solid var(--violet);outline-offset:3px;
  border-radius:var(--r)}

/* ---- avatars ---- */
.av{width:56px;height:56px;border-radius:50%;flex:0 0 auto;overflow:hidden;
  display:grid;place-items:center;font-weight:700;letter-spacing:.5px;
  color:#fff;user-select:none;border:1px solid var(--line)}
.av img{width:100%;height:100%;object-fit:cover;display:block}
/* No picture: initials on a colour derived from the account id, so one person
   always looks the same and two people rarely look alike. */
.av-init{font-size:19px}
.av-lg{width:104px;height:104px}
.av-lg .av-init{font-size:35px}
.av-sm{width:34px;height:34px}
.av-sm .av-init{font-size:13px}
.avbar{display:flex;gap:20px;align-items:center;flex-wrap:wrap;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:18px 22px;margin-bottom:16px;backdrop-filter:blur(8px)}
.avbar-body{flex:1;min-width:260px;display:flex;flex-direction:column;gap:6px}
.avbar-body b{font-size:15px}
.avbar .ph-add{margin-top:4px;align-items:center}
.avbar .ph-add form{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
/* The listing card puts the avatar beside the name, so the head is a row. */
.mcard-head{align-items:center}

/* ---- comments ---- */
.thread{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.thread-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;font-size:13.4px}
.thread-head b{color:var(--muted)}
.thread-head .fhint{margin:0}
/* Only the overflow folds. The form is never behind a disclosure — hiding it
   was what made the first comment on a service impossible to leave. */
.more{margin-top:10px}
.more summary{cursor:pointer;font-size:13px;color:var(--violet);list-style:none;
  display:flex;align-items:center;gap:8px;padding:4px 0;transition:color .18s}
.more summary::before{content:"▸"}
.more[open] summary::before{content:"▾"}
.more summary:hover{color:var(--text)}
.cmt-signin{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:12px}
.cmt{background:rgba(0,0,0,.2);border:1px solid var(--line);border-radius:var(--r);
  padding:13px 16px;margin-top:10px}
/* Hidden by a moderator: still legible to those who may see it, visibly not live. */
.cmt.off{opacity:.6;border-style:dashed}
.cmt-head{display:flex;align-items:flex-start;gap:11px;margin-bottom:7px;font-size:13.4px}
.cmt-who{flex:1;min-width:0}
.cmt-line1{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.cmt-line1 .when{margin-left:auto;font-size:12px;color:var(--muted-2)}
.cmt-about{margin-top:2px;font-size:12.5px}
.cmt-about a{color:var(--violet);text-decoration:none}
.cmt-about a:hover{text-decoration:underline}
.cmt-about .cmt-svc{color:var(--muted-2)}
/* Avatar or initials, same footprint either way so rows line up. */
.cmt-av{flex:0 0 auto;width:38px;height:38px;border-radius:50%;overflow:hidden;
  background:var(--panel-2);border:1px solid var(--line);
  display:flex;align-items:center;justify-content:center}
.cmt-av img{width:100%;height:100%;object-fit:cover}
.cmt-av-i{font-size:13px;font-weight:700;color:var(--violet)}
.cmt p{font-size:14px;color:var(--read);line-height:1.65;white-space:pre-wrap}
.stars{color:var(--gold);font-size:13px;letter-spacing:1px}
.cmt-reply{margin-top:10px;padding:10px 13px;border-left:2px solid var(--violet);
  background:rgba(124,58,237,.09);border-radius:0 var(--r) var(--r) 0}
.cmt-reply b{font-size:12.5px;color:var(--violet);display:block;margin-bottom:4px}
.cmt-acts{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:10px}
.cmt-form{margin-top:14px;display:flex;flex-direction:column;gap:9px}
.cmt-form-row{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap}
.cmt-form-row .btn{margin-left:auto}

/* ---- public reader listing ---- */
.mgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:16px}
.mcard{display:flex;flex-direction:column;gap:10px;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-lg);padding:20px 22px;
  backdrop-filter:blur(8px);transition:.18s}
.mcard:hover{border-color:var(--violet);transform:translateY(-2px)}
.mcard-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.mcard-head h3{font-size:17.5px;font-weight:700}
.mcard .hl{font-size:14px;color:var(--violet)}
.mcard .teaser{font-size:13.6px;color:var(--read);line-height:1.65;flex:1}
.mcard-foot{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding-top:12px;border-top:1px solid var(--line);font-size:13px;color:var(--muted)}
.mcard-foot .price{color:var(--gold);font-weight:700;margin-left:auto}

/* The landing leaderboard. A `.mcard` with a rank in front of the avatar and
   one number under it — no price, no teaser: this block answers "who is busy
   right now", and anything else is the readers page's job. */
.toprank{flex:0 0 auto;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;
  font-size:12.5px;font-weight:700;color:var(--gold);border:1px solid rgba(245,197,66,.35);
  background:rgba(245,197,66,.09)}
/* First place is the only one worth painting; below it a ladder of shades just
   makes fourth look broken. */
.topcard:first-child .toprank{color:#1b1430;background:var(--gold);border-color:var(--gold)}
.topcard .mcard-head{align-items:center}
.topcard .mcard-foot{border-top:0;padding-top:0;margin-top:2px}
.topdeals{font-weight:700;color:var(--read)}
.topmore{margin:16px 0 0;text-align:center;font-size:14px}
.badge-on{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;letter-spacing:.6px;
  text-transform:uppercase;color:var(--ok,#4ADE80)}
.badge-off{font-size:11.5px;letter-spacing:.6px;text-transform:uppercase;color:var(--muted-2)}
/* The VIP counter bar: one line, the whole status, and the way in. */
.vipbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:14px 18px;margin-bottom:28px;backdrop-filter:blur(8px);font-size:14px}
.vipbar.on{border-color:rgba(245,197,66,.30);
  border-color:color-mix(in srgb, var(--gold) 30%, var(--line));
  background:linear-gradient(180deg,rgba(245,197,66,.05),transparent 70%),var(--panel);
  background:linear-gradient(180deg,color-mix(in srgb, var(--gold) 6%, transparent),transparent 70%),var(--panel)}
.vipbar-left{font-weight:700}
.vipbar-left.soon{color:var(--gold)}
/* Pushed to the end, so the button is in the same place whatever the line says
   in between — three different states share this row. */
.vipbar-go{margin-left:auto;flex:none}

/* The window. `<dialog open>` without showModal() is an ordinary block in the
   flow and gets no ::backdrop, so the scrim is a real element and the dialog is
   positioned itself. That is the price of working with no JavaScript, and it is
   worth paying: the button is a link, the address says what is open, and Back
   closes it. */
.vipdlg-scrim{position:fixed;inset:0;z-index:60;background:rgba(6,4,14,.78);
  display:block}
.vipdlg{position:fixed;z-index:61;inset:0;margin:auto;width:min(560px,92vw);
  max-height:86vh;padding:0;border:1px solid var(--line-2);border-radius:16px;
  background:var(--panel-solid);color:var(--text);overflow:auto}
.vipdlg-in{display:flex;flex-direction:column;gap:10px;padding:16px 20px 20px}
.vipdlg-head{display:flex;align-items:center;gap:9px;font-size:16px}
.vipdlg-head .rdlg-x{text-decoration:none}
.vipdlg .vip-claims{margin:6px 0 4px}
.vipdlg .vip-buy{margin-top:4px}
.vipdlg-price{font-weight:700;color:var(--gold)}

/* The premium mark. Same chip as VIP, violet instead of gold, because it is a
   different status — two gold badges side by side would read as one thing said
   twice. */
.prem{display:inline-flex;align-items:center;gap:4px;color:var(--violet);
  font-size:10.5px;font-weight:800;letter-spacing:.9px;text-transform:uppercase;
  padding:3px 8px 3px 6px;border-radius:100px;
  background:rgba(167,139,250,.10);
  background:color-mix(in srgb, var(--violet) 12%, transparent);
  border:1px solid rgba(167,139,250,.28);
  border-color:color-mix(in srgb, var(--violet) 34%, transparent);
  white-space:nowrap;flex:none}
.prem b{font-weight:800}

/* ---- VIP ----
   A bought placement, so it has to read as *distinct* without reading as
   *louder*. The gold is the site's existing accent, already defined for both
   themes; the highlight is a warmer border and a faint wash, not a glow — a
   listing page where the paid entries shout is a listing page people learn to
   scroll past. */
.vip{display:inline-flex;align-items:center;gap:4px;color:var(--gold);
  font-size:10.5px;font-weight:800;letter-spacing:.9px;text-transform:uppercase;
  padding:3px 8px 3px 6px;border-radius:100px;
  /* colour-mix so the chip tints itself from --gold and stays correct when the
     theme redefines it. The fallback matters: colour-mix is unsupported on
     older WebKit, where an undefined background is simply no background and
     the chip still reads fine. */
  background:rgba(245,197,66,.10);
  background:color-mix(in srgb, var(--gold) 12%, transparent);
  border:1px solid rgba(245,197,66,.28);
  border-color:color-mix(in srgb, var(--gold) 34%, transparent);
  white-space:nowrap;flex:none}
.vip-i{flex:none;display:block}
.vip b{font-weight:800}

/* The card itself. `.is-vip` is added alongside the existing class rather than
   replacing it, so every rule above still applies and only the accent moves. */
.mcard.is-vip,.ocard.is-vip{
  border-color:rgba(245,197,66,.34);
  border-color:color-mix(in srgb, var(--gold) 34%, var(--line));
  /* The wash follows --gold too. Hard-coding the dark gold here painted a
     yellow tint over a white panel in light mode while the border beside it
     went brown — the two disagreeing on the same card. */
  background:linear-gradient(180deg,rgba(245,197,66,.055),transparent 62%),var(--panel);
  background:linear-gradient(180deg,color-mix(in srgb, var(--gold) 6%, transparent),transparent 62%),var(--panel)}
.mcard.is-vip:hover,.ocard.is-vip:hover{
  border-color:rgba(245,197,66,.6);
  border-color:color-mix(in srgb, var(--gold) 60%, transparent)}

/* On the marketplace the badge sits next to the master's name inside the
   `.ocard-who` link, which is a flex row that may already be tight. */
.ocard-who .vip{padding:2px 6px 2px 5px;font-size:9.5px;letter-spacing:.7px}

@media (prefers-reduced-motion:no-preference){
  .mcard.is-vip,.ocard.is-vip{transition:.18s}
}
/* The VIP panel on the master dashboard, and the admin tab's rows. */
.vipbox{border-color:rgba(245,197,66,.22);
  border-color:color-mix(in srgb, var(--gold) 22%, var(--line))}
.vip-h{padding:0;border:0;background:none;margin-right:2px;vertical-align:-1px}
.vip-h3{font-size:14px;font-weight:700;margin:18px 0 8px}

/* Where they stand. Gold only when it is actually true — a panel that looks
   the same whether or not you hold the thing is a panel nobody trusts. */
.vip-state{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:12px 15px;border-radius:var(--r);border:1px solid var(--line);
  background:rgba(0,0,0,.16);font-size:14px}
.vip-state.on{border-color:rgba(245,197,66,.34);
  border-color:color-mix(in srgb, var(--gold) 34%, transparent);
  background:rgba(245,197,66,.06);
  background:color-mix(in srgb, var(--gold) 7%, transparent)}
.vip-warn{font-size:12.5px;color:var(--gold)}

/* The three claims, each ticked or not. `::before` rather than a character in
   the markup, so the tick is not read out by a screen reader as a word and not
   copied when somebody selects the text. */
.vip-claims{list-style:none;margin:0 0 12px;padding:0;display:grid;gap:7px}
.vip-claims li{position:relative;padding-left:26px;font-size:13.6px;line-height:1.6;
  color:var(--muted)}
.vip-claims li::before{position:absolute;left:0;top:0;width:18px;text-align:center;
  font-weight:700}
.vip-claims li.yes{color:var(--read)}
.vip-claims li.yes::before{content:"\2713";color:var(--gold)}
.vip-claims li.no::before{content:"\00B7";color:var(--muted-2);font-size:20px;line-height:1}

.vip-gate{margin:6px 0 14px;font-size:13.6px;color:var(--muted);line-height:1.6}
/* `display:block` is not decoration. The bar lives inside a <li> as a <span>
   now, and an inline element ignores height — it came out 87px tall, sized by
   the line box, with the fill painting over the text beside it. */
.vip-bar{display:block;height:6px;border-radius:100px;background:rgba(0,0,0,.28);
  overflow:hidden;margin:7px 0 2px;max-width:420px}
.vip-bar span{display:block;height:100%;border-radius:100px;background:var(--gold)}

.vip-buy{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;margin-top:14px}
.vip-buy .mini{min-width:230px}
.vip-buy .fhint{flex-basis:100%;margin:0}

.vipgrant{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;margin-bottom:6px}

/* Small state word next to a row: live / lapsed / comped. */
.vip-tag{font-size:10.5px;font-weight:700;letter-spacing:.7px;text-transform:uppercase;
  padding:2px 7px;border-radius:100px;border:1px solid var(--line);color:var(--muted-2)}
.vip-tag.on{color:var(--gold);border-color:rgba(245,197,66,.34);
  border-color:color-mix(in srgb, var(--gold) 34%, transparent)}

/* A row for something that is over. Still readable — an admin list is history
   as much as it is state — just not competing with the live ones. */
.dim{opacity:.55}
.mfilter{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:24px}
.mfilter a{font-size:13.4px;padding:7px 15px;border-radius:100px;border:1px solid var(--line);
  color:var(--muted);transition:.18s}
.mfilter a:hover{border-color:var(--violet);color:var(--text)}
.mfilter a.on{background:var(--violet-d);border-color:var(--violet-d);color:#fff;font-weight:600}
.mfilter a .n{opacity:.6;font-size:11.5px;margin-left:5px}

/* One filter field, shared by the marketplace sidebar and the readers page.
   Both ask the same questions in the same shapes, so the field styling is here
   rather than copied into each page's own <style>. */
.mkt-f{display:flex;flex-direction:column;gap:6px;min-width:0}
.mkt-f>span{font-size:11.5px;letter-spacing:.4px;text-transform:uppercase;color:var(--muted-2)}
.mkt-f input,.mkt-f select{width:100%;background:var(--panel-2);border:1px solid var(--line);
  color:var(--text);border-radius:9px;padding:8px 11px;font:inherit;font-size:14px;min-width:0}
.mkt-f input:focus,.mkt-f select:focus{outline:0;border-color:var(--violet)}
.mkt-act{display:flex;gap:8px;padding-top:2px}
.mkt-act .btn{flex:1;justify-content:center}
.mkt-hint{font-size:12.5px;color:var(--muted-2);margin:0;line-height:1.55}

/* The readers page asks its two questions in a row under the section chips,
   rather than in a sidebar: this page has no sidebar to put them in, and two
   fields do not earn one. */
.mfilter2{display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;margin:0 0 10px}
.mfilter2 .mkt-f{flex:1 1 200px;max-width:280px}
.mfilter2 .mkt-act{flex:0 0 auto;padding-top:0}
.mfilter2 .mkt-act .btn{flex:0 0 auto}
/* Its own class, not `.mfilter2 + .mkt-hint`: the countries <datalist>
   sits between the two and an adjacent-sibling selector would never
   match — invisibly, since the rule is only spacing. */
.mfilter2-note{margin:0 0 18px}

/* What a reader speaks, on their card and in the marketplace. Quiet — it is a
   qualifying fact, not a selling point, and it sits above the pitch. */
.mspeak{font-size:13px;color:var(--muted);display:flex;gap:6px;align-items:baseline}
.mspeak span{opacity:.75;flex:0 0 auto}
.ospeak{display:block}

/* The languages checkbox grid in the profile editor. Fifty-odd entries: a
   column count that follows the width, and each one a full click target. */
.langpick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));
  gap:2px 14px;margin-bottom:6px}
.langpick-one{display:flex;align-items:center;gap:7px;font-size:13.5px;color:var(--read);
  padding:5px 6px;border-radius:8px;cursor:pointer;min-width:0}
.langpick-one:hover{background:var(--panel-2)}
.langpick-one input{accent-color:var(--violet-d);flex:0 0 auto}
.langpick-one span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- one master's public page ---- */
.mhead{display:flex;gap:18px;align-items:flex-start;flex-wrap:wrap;margin-bottom:10px}
.mhead .who h1{margin-bottom:4px}
.offer{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:18px 20px;margin-bottom:12px;backdrop-filter:blur(8px)}
.offer-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:8px}
.offer-head h3{font-size:16.5px;font-weight:700;flex:1;min-width:200px}
.offer-head .price{color:var(--gold);font-weight:700;font-size:16px;white-space:nowrap}
.offer p{font-size:14px;color:var(--read);line-height:1.7;white-space:pre-wrap}
.offer .meta{display:flex;gap:14px;flex-wrap:wrap;margin-top:10px;font-size:12.5px;color:var(--muted-2)}

/* ---- demo chrome (mockups only — never ships) ---- */
.demo{position:fixed;right:16px;bottom:16px;z-index:80;background:#0E0722;
  border:1px solid var(--line-2);border-radius:var(--r);padding:13px 15px;
  font-family:var(--mono);font-size:11.5px;color:var(--muted);
  box-shadow:0 12px 34px rgba(0,0,0,.6);max-width:250px}
.demo b{display:block;color:var(--gold);font-size:10px;letter-spacing:1.6px;
  text-transform:uppercase;margin-bottom:9px}
.demo .row{display:flex;gap:7px;flex-wrap:wrap;margin-top:9px}
.demo button{background:var(--panel);border:1px solid var(--line);color:var(--text);
  border-radius:7px;padding:5px 10px;font-family:var(--mono);font-size:11px;cursor:pointer}
.demo button:hover{border-color:var(--violet);background:var(--panel-2)}
.demo button.on{background:var(--violet-d);border-color:var(--violet-d);color:#fff}

/* ---- dashboard shell (logged-in chrome) ---- */
.appbar{position:sticky;top:0;z-index:40;background:rgba(10,5,24,.85);backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line)}
.appbar-in{max-width:var(--wrap);margin:0 auto;padding:13px 24px;display:flex;align-items:center;gap:22px;flex-wrap:wrap}
.appbar .tabs{display:flex;gap:6px;margin-left:14px}
.appbar .tabs a{font-size:13.8px;color:var(--muted);padding:7px 14px;border-radius:100px;transition:.18s}
.appbar .tabs a:hover{color:var(--text);background:var(--panel)}
.appbar .tabs a.on{background:var(--violet-d);color:#fff;font-weight:600}
.appbar .who{margin-left:auto;display:flex;align-items:center;gap:12px;font-size:13.8px;color:var(--muted)}
.appbar form{display:inline}
.linkbtn{background:none;border:0;color:var(--muted);font:inherit;font-size:13.8px;cursor:pointer;padding:0}
.linkbtn:hover{color:var(--danger)}

.dash{padding:36px 0 60px}
.dash h1{font-size:28px;font-weight:700;letter-spacing:-.8px;margin-bottom:6px}
.dash .lede{color:var(--muted);font-size:15.5px;margin-bottom:28px}

.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:16px;margin-bottom:32px}
.tile{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:20px 22px;backdrop-filter:blur(8px)}
.tile .k{font-size:11.5px;letter-spacing:1.6px;text-transform:uppercase;color:var(--muted);margin-bottom:7px}
.tile .v{font-size:27px;font-weight:700;letter-spacing:-.8px;font-variant-numeric:tabular-nums}
.tile .v.gold{color:var(--gold)}
.tile .v.ok{color:var(--ok)}
.tile .sub{font-size:12.5px;color:var(--muted-2);margin-top:5px}

.sect{margin-bottom:34px}
.sect h2{font-size:19px;font-weight:700;letter-spacing:-.4px;margin-bottom:14px}
.rowlist{border-top:1px solid var(--line)}
.rowitem{display:flex;align-items:center;gap:14px;padding:13px 0;border-bottom:1px solid var(--line);
  font-size:14.2px;flex-wrap:wrap}
.rowitem .grow{flex:1;min-width:180px}
.rowitem .muted2{font-size:12.5px;color:var(--muted)}
.empty{color:var(--muted);font-size:14.2px;padding:18px 0;font-style:italic}

.pill-status{display:inline-block;border-radius:100px;padding:3px 11px;font-size:11.5px;font-weight:700;
  letter-spacing:.3px;white-space:nowrap}
.st-pending{background:rgba(251,191,36,.16);color:var(--warn)}
.st-approved{background:rgba(52,211,153,.16);color:var(--ok)}
.st-rejected{background:rgba(248,113,113,.16);color:var(--danger)}
.st-suspended{background:rgba(248,113,113,.16);color:var(--danger)}

.meter-bar{height:7px;border-radius:100px;background:rgba(255,255,255,.08);overflow:hidden;margin-top:9px}
.meter-bar i{display:block;height:100%;background:var(--ok);border-radius:100px}
.meter-bar i.warn{background:var(--warn)}
.meter-bar i.crit{background:var(--danger)}

/* ---- utilities ---- */
.mono{font-family:var(--mono)}
.muted{color:var(--muted)}
.gold{color:var(--gold)}
.center{text-align:center}
.row{display:flex;align-items:center}
.gap-8{gap:8px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-24{gap:24px}
.mla{margin-left:auto}
.grid-auto{display:grid;gap:20px;grid-template-columns:repeat(auto-fill,minmax(248px,1fr))}
.hide-sm{display:initial}

@media(max-width:760px){
  .nav-links{display:none}
  .hide-sm{display:none}
  .sec{padding:52px 0}
}

/* An edit is never silent: the note sits directly under the text it describes. */
.cmt-edited{font-size:12px;color:var(--muted-2);font-style:italic;margin-top:7px;
  padding-top:6px;border-top:1px dashed var(--line)}
.cmt-acts .pencil{color:var(--violet)}
.cmt-acts .pencil:hover{color:var(--text)}
/* Edit mode is a different state and should look like one. */
.cmt-form.editing{margin-top:0;padding:12px 14px;border-radius:var(--r);
  background:rgba(124,58,237,.08);border:1px solid var(--line-2)}
.cmt-form.editing .cmt-form-row .btn{margin-left:0}
.cmt-form.editing .cmt-form-row:last-of-type{justify-content:flex-end}

/* ---- card collection ---- */
/* Capped so ten cards fall as two rows of five. Left to fill the container
   they wrapped 6+4, which reads as a mistake rather than a layout. */
.pickrow{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin:0 auto 8px;
  max-width:604px}
.reveal-band{text-align:center;margin:0 auto 30px;max-width:820px;
  border:1px solid var(--line-2);border-radius:var(--r-lg);
  background:rgba(124,58,237,.09);padding:26px 24px}
.reveal-band h3{font-size:20px;font-weight:700;margin-bottom:6px}
.reveal-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin:18px 0 4px}
.progress{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:24px}
.progress .meter-bar{flex:1;min-width:220px}
.progress-n{font-size:14px;color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}
.progress-n b{color:var(--gold);font-size:19px}
.shelf{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:10px}

/* One card. Everything below is a variant of this box. */
.tc{width:100%;aspect-ratio:11/17;border-radius:var(--r);border:1px solid var(--line);
  background:var(--panel);display:grid;place-items:center;padding:0;overflow:hidden;
  position:relative;transition:.2s}
.tc-in{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:4px;padding:8px 6px;text-align:center;width:100%;height:100%}
.tc .sym{font-size:26px;color:var(--gold);line-height:1}
.tc .num{font-size:9.5px;letter-spacing:1.4px;color:var(--muted-2)}
.tc .nm{font-size:11px;font-weight:600;color:var(--read);line-height:1.3}
.tc .dupe{font-size:10px;color:var(--gold);font-weight:700}
/* How many copies you hold, on a shelf tile — a chip in the bottom-right
   corner. The template only draws it for two or more (`duplicate()` is
   `copies > 1`), so a single card wears nothing. Absolute over the art, with
   its own dark backing and a thin gold edge, because gold text on its own
   disappears against a bright card face. Scoped to `.tc-have`, the owned tiles,
   so the big "just picked" reveal keeps its inline "another copy" label. */
.tc-have .dupe{position:absolute;right:6px;bottom:6px;z-index:2;margin:0;
  min-width:20px;height:20px;padding:0 6px;border-radius:10px;
  display:flex;align-items:center;justify-content:center;
  font-size:11.5px;font-weight:700;line-height:1;color:var(--gold);
  background:rgba(8,5,20,.85);border:1px solid rgba(245,197,66,.55);
  box-shadow:0 1px 5px rgba(0,0,0,.45)}

/* Held. */
.tc-have{background:linear-gradient(160deg,rgba(124,58,237,.16),rgba(0,0,0,.25));
  border-color:var(--line-2)}
.tc-have:hover{transform:translateY(-3px);border-color:var(--violet)}
/* Not held yet — present but plainly empty, so the gap is the point. */
.tc-miss{background:rgba(0,0,0,.22);border-style:dashed}
.tc-miss span{font-size:20px;color:var(--muted-2);opacity:.5}

/* Face down and clickable. */
.tc-back{width:104px;cursor:pointer;
  background:linear-gradient(150deg,#1B0F3D,#0E0722);border-color:var(--line-2);
  animation:tc-in .45s cubic-bezier(.3,.9,.4,1) both}
.tc-back:hover{transform:translateY(-6px) rotate(-1.5deg);border-color:var(--violet);
  box-shadow:0 14px 34px rgba(124,58,237,.4)}
.tc-back:active{transform:translateY(-2px)}

/* Just taken: flips face-up on load, so the reveal needs no JavaScript. The
   card is drawn back-first and rotates through 90° where it is edge-on. */
.tc-got{width:104px;background:linear-gradient(160deg,rgba(245,197,66,.2),rgba(124,58,237,.2));
  border-color:var(--gold);box-shadow:0 10px 34px rgba(245,197,66,.25);
  animation:tc-flip .7s cubic-bezier(.4,.2,.2,1) both}
.tc-big{width:150px}
.tc-big .sym{font-size:38px}
.tc-big .nm{font-size:13.5px}
/* The face is hidden for the first half of the turn, while the back is toward
   the viewer. */
.tc-got .tc-in{animation:tc-face .7s step-end both}

@keyframes tc-flip{
  0%{transform:perspective(900px) rotateY(180deg) scale(.9);opacity:.4}
  60%{opacity:1}
  100%{transform:perspective(900px) rotateY(0) scale(1);opacity:1}
}
@keyframes tc-face{0%,49%{opacity:0}50%,100%{opacity:1}}
@keyframes tc-in{from{transform:translateY(10px);opacity:0}to{transform:none;opacity:1}}

/* Respect a stated preference for less motion. */
@media(prefers-reduced-motion:reduce){
  .tc-back,.tc-got,.tc-got .tc-in{animation:none}
  .tc-back:hover{transform:none}
}
@media(max-width:640px){
  .tc-back,.tc-got{width:84px}
  .tc-big{width:120px}
  .shelf{grid-template-columns:repeat(auto-fill,minmax(84px,1fr))}
}

/* Card artwork fills its frame; the glyph rendering is the fallback when the
   file is not there yet, so both have to sit in the same box. */
.tc-art{width:100%;height:100%;object-fit:cover;display:block;border-radius:inherit}
.tc:has(.tc-art) .tc-in{padding:0}
/* The reveal names the card under the picture, which the art itself may not. */
.tc-cap{position:absolute;left:0;right:0;bottom:0;padding:16px 6px 6px;
  font-size:11.5px;font-weight:600;color:#fff;text-align:center;
  background:linear-gradient(to top,rgba(6,2,16,.9),transparent)}
.tc-big .tc-cap{font-size:13px}

/* ---- the table ------------------------------------------------------------
   Where you lay a spread out of your own collection. Three places on a felt,
   your cards underneath, and no shuffling anywhere — the whole point is that
   you chose. */
.tbl-felt{border:1px solid var(--line);border-radius:var(--r-xl);padding:26px 18px;
  background:
    radial-gradient(70% 90% at 50% 0%,rgba(124,58,237,.18),transparent 70%),
    linear-gradient(160deg,rgba(21,10,51,.75),rgba(10,5,24,.85))}
.tbl-row{display:flex;gap:20px;justify-content:center;flex-wrap:wrap}
.tbl-place{display:flex;flex-direction:column;align-items:center;gap:10px;
  width:min(30vw,190px);margin:0}
.tbl-slot{font-size:11.5px;letter-spacing:2px;text-transform:uppercase;color:var(--violet)}
/* Perspective sits on the PLACE, not on the row: it applies to an element's
   children, and the card is the place's child. On the row it would reach the
   place and stop, and every turn would render as a flat squash. */
.tbl-place{perspective:1200px}
.tbl-card{width:100%;aspect-ratio:2/3;position:relative;transform-style:preserve-3d;
  transition:transform .5s cubic-bezier(.34,1.28,.5,1)}
/* No filter and no opacity on this element, ever. Both are grouping
   properties and force preserve-3d flat, which collapses the two faces into
   one and the card stops having a back. */
.tbl-face{position:absolute;inset:0;border-radius:var(--r-lg);overflow:hidden;
  border:1px solid var(--line);background:linear-gradient(160deg,#170a2e,#0d0520);
  box-shadow:0 10px 26px rgba(0,0,0,.5);
  backface-visibility:hidden;-webkit-backface-visibility:hidden}
.tbl-front{transform:rotateY(0deg)}
.tbl-back-face{transform:rotateY(180deg);background:
  radial-gradient(circle at 50% 42%,rgba(167,139,250,.16),transparent 62%),
  linear-gradient(160deg,#1b0d38,#0d0520)}
.tbl-back-face img{width:100%;height:100%;object-fit:cover;display:block}
.tbl-back-face svg{width:56%;height:56%;position:absolute;left:22%;top:22%}
/* Reversed turns the picture inside the front face — a different rotation on
   a different axis from the turn-over, and they must not share a property. */
.tbl-card.rev .tbl-in{transform:rotate(180deg)}
.tbl-in{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:8px;text-align:center;padding:12px;
  transition:transform .5s cubic-bezier(.34,1.28,.5,1)}
.tbl-in img{width:100%;height:100%;object-fit:cover;display:block}
.tbl-in .sym{font-size:40px;line-height:1;color:var(--violet)}
.tbl-in .num{font-size:11px;letter-spacing:2px;color:var(--muted-2)}
.tbl-in .nm{font-size:13px;font-weight:600;color:#fff}
.tbl-name{margin:0;font-size:14px;font-weight:600;color:#fff;text-align:center}
.tbl-rev{font-size:11px;font-weight:500;color:var(--violet);white-space:nowrap}
.tbl-mean{margin:0;font-size:13px;line-height:1.5;color:var(--muted);text-align:center}
.tbl-row-done .tbl-place{width:min(38vw,210px)}

.tbl-gate-n{font-size:30px;font-weight:700;color:var(--violet);margin-bottom:8px;
  font-variant-numeric:tabular-nums}
.tbl-meter{text-align:center;font-size:13.5px;color:var(--muted);margin-bottom:18px}

/* Spread picker: choose the shape before laying. */
.tbl-picker{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin:4px 0}
.tbl-pick{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:74px;
  padding:10px 14px;border:1px solid var(--line);border-radius:14px;text-decoration:none;
  background:var(--panel-2);color:var(--muted);transition:.15s}
.tbl-pick:hover{border-color:var(--violet);color:var(--text)}
.tbl-pick.on{border-color:var(--violet);background:rgba(124,58,237,.16);color:#fff}
.tbl-pick.short{opacity:.5}
.tbl-pick-n{font-size:20px;font-weight:700;color:var(--gold);line-height:1}
.tbl-pick.on .tbl-pick-n{color:var(--violet)}
.tbl-pick-name{font-size:11px;text-align:center}

/* A laid spread on its own grid. Cells are card-width (auto), centred as a whole. */
.tbl-grid{display:grid;gap:16px;justify-content:center;align-items:center}
.tbl-grid .tbl-place{width:min(20vw,132px)}
.tbl-place{position:relative}
.tbl-num{position:absolute;top:-8px;left:-8px;z-index:6;width:22px;height:22px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;
  background:var(--violet-d,#5b21b6);color:#fff;border:1px solid var(--gold)}
/* The Celtic Cross's crossing card: laid sideways across the one before it, in
   the same grid cell, on top. Scaled a touch so it doesn't swamp the arms. */
.tbl-place.cross{z-index:4}
.tbl-place.cross .tbl-card{transform:rotate(90deg) scale(.86)}
.tbl-place.cross .tbl-num{top:auto;bottom:-8px;left:auto;right:-8px}

/* The reading, read out position by position. */
.tbl-legend{list-style:none;margin:26px auto 0;padding:0;max-width:640px;
  display:flex;flex-direction:column;gap:10px}
.tbl-legend li{display:flex;gap:12px;align-items:flex-start;padding:10px 13px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel-2)}
.tbl-legend-n{flex:0 0 auto;font-size:13px;font-weight:700;color:var(--gold);min-width:26px;padding-top:1px}
.tbl-legend-body{min-width:0}
.tbl-legend-slot{margin:0;font-size:14px;color:#fff}
.tbl-legend-mean{margin:3px 0 0;font-size:13px;line-height:1.5;color:var(--muted)}
@media(max-width:640px){.tbl-grid{gap:9px}.tbl-grid .tbl-place{width:min(21vw,104px)}}

@media(max-width:640px){
  .tbl-row{gap:12px}
  .tbl-place{width:min(42vw,150px)}
  .tbl-felt{padding:18px 10px}
}
@media(prefers-reduced-motion:reduce){
  .tbl-card,.tbl-in{transition:none}
  .tbl-row-done .tbl-place{animation:none}
}

/* The stack that gets shuffled and dealt from. Absolutely placed over the
   felt so the cards below keep their layout — the deal has to fly to where
   they already are, not to where they would be if it moved them. */
/* ALWAYS in the flow. Absolutely positioned it contributed no height at all,
   the felt collapsed to the height of the line of text under it, and the
   stack hung 40px past the top of the button below. Whatever else changes
   here, it keeps its own space. */
/* Sized to one card; the pile spreads out of it and the script pads for that.
   box-sizing is content-box here on purpose: the width is the CARD's width,
   and the padding is the room the rest of the pile needs beside it. */
.tbl-deck{position:relative;margin:20px auto 0;display:block;padding:0;
  box-sizing:content-box;width:min(30vw,150px);height:calc(min(30vw,150px) * 1.5);
  z-index:2;background:none;border:0}
.tbl-deck[hidden]{display:none}
/* The toy on the first screen: press it and it shuffles. */
.tbl-deck{cursor:pointer}
.tbl-deck:focus-visible{outline:2px solid var(--violet);outline-offset:6px;
  border-radius:var(--r-lg)}
/* The pile is fanned enough to read as layers. Without that, moving a packet
   from the top to the middle changes a z-index and nothing visible: every card
   is in the same place, so "it went underneath" cannot be seen. */
.tbl-deck{--fan:1}
/* On the result it sits under the spread — in the flow, below the row — so the
   cards travel UP to their places. It is pressable: it was decoration once and
   carried pointer-events:none from then, which left a deck with a "tap to
   shuffle" hint under it that could not be tapped. */
.tbl-deck-live{margin-top:26px}
/* Every card needs an edge you can see against the card behind it. With the
   old near-invisible border, seven overlapping dark rectangles rendered as one
   dark rectangle — which is why nothing in the pile ever looked like a layer.
   The brightness ramp that separates them is set per card by table.js; this is
   the outline that gives each one a boundary. */
.tbl-back{position:absolute;left:0;top:0;width:min(30vw,150px);aspect-ratio:2/3;
  border-radius:var(--r-lg);overflow:hidden;
  border:1px solid rgba(167,139,250,.55);background:
    radial-gradient(circle at 50% 42%,rgba(167,139,250,.16),transparent 62%),
    linear-gradient(160deg,#1b0d38,#0d0520);
  /* A tight dark shadow right under the edge reads as the next card down being
     a separate object, which a soft wide one does not. */
  box-shadow:0 2px 0 rgba(0,0,0,.5),0 10px 26px rgba(0,0,0,.45);
  transform:translate(0,0)}
.tbl-back img{width:100%;height:100%;object-fit:cover;display:block}
.tbl-back svg{width:56%;height:56%;position:absolute;left:22%;top:22%}
/* The felt is the frame the stack is centred in. */
.tbl-felt{position:relative}
/* A card mid-throw must sit above the felt's own background. */
/* ABOVE the deck, which is z-index 2.
 *
 * This was 1, so the pile painted on top of the spread — and because the row
 * carries a z-index it makes a stacking context, which no z-index on a card
 * inside it can escape. A card thrown from the deck therefore began its flight
 * BEHIND the pile and slid out from underneath it, which is exactly what
 * "the cards are literally going from the very bottom" describes. Every
 * measurement said the throw started on the top card, and every one of them
 * was about geometry while the complaint was about paint order. */
.tbl-row-done{position:relative;z-index:3}

/* Handing cards out, in the admin user card. Its own row under the flags so
   the two submit buttons are visibly separate things. */
.ucards{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px;
  padding-top:12px;border-top:1px solid var(--line)}
.ucards-l{font-size:12px;letter-spacing:.4px;text-transform:uppercase;color:var(--muted)}
.ucards select{flex:1 1 220px;max-width:320px;padding:8px 11px;font-size:13.5px}
.ucards input[type=number]{width:78px;padding:8px 11px;font-size:13.5px}
.ucards-h{font-size:11.5px;color:var(--muted-2);flex:1 1 100%}

/* The deck cuts rather than jitters, so its cards move under their own inline
   transforms rather than a class. */
.tbl-back{transition:transform .42s cubic-bezier(.3,.05,.3,1),
                     filter .42s cubic-bezier(.3,.05,.3,1)}
/* Clear of the stack: the backs cast 0 10px 26px, so anything within about
   36px of the bottom card sits in their shadow. */
.tbl-dealbar{display:flex;flex-direction:column;align-items:center;gap:8px;
  margin-top:52px;transition:opacity .3s}
.tbl-dealbar.gone{opacity:0;pointer-events:none}
.tbl-dealhint{font-size:12.5px;color:var(--muted-2);text-align:center}
@media(prefers-reduced-motion:reduce){.tbl-back{transition:none}}
/* The room for the spread is set by table.js, from the same numbers that
   produce it — see the note there. Without the script there is no spread and
   no padding is wanted. */

/* ---- news ---- */
.nwlist{display:grid;gap:16px;max-width:760px;margin:0 auto}
.nwcard{border:1px solid var(--line);border-radius:var(--r-lg);padding:20px 22px;
  background:var(--panel);backdrop-filter:blur(8px);transition:.2s}
.nwcard:hover{border-color:var(--line-2)}
.nwdate{font-size:12px;letter-spacing:1.6px;text-transform:uppercase;color:var(--muted-2)}
.nwcard h2{font-size:20px;font-weight:700;margin:6px 0 8px;letter-spacing:-.3px}
.nwcard h2 a:hover{color:var(--violet)}
.nwteaser{font-size:14.5px;color:var(--muted);line-height:1.65}
.nwmore{display:inline-block;margin-top:10px;font-size:13.5px;color:var(--violet)}
.nwfull{padding:20px 0 60px}
.nwfull h1{font-size:clamp(26px,4vw,38px);font-weight:700;letter-spacing:-1px;margin:8px 0 22px}
.nwfull p{font-size:16px;line-height:1.75;color:var(--read);margin-bottom:16px}
.nwby{font-size:13px;color:var(--muted-2);margin-top:26px}
/* admin */
.nwform{background:rgba(0,0,0,.18);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:18px 20px;margin-bottom:16px}
.nwhint{font-size:12px;color:var(--muted-2);margin-top:6px}
.nwrow{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin-top:14px}
.nwlang{display:flex;align-items:center;gap:8px}
.nwlang select{width:auto;padding:8px 11px;font-size:13.5px}
.nwrowitem{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap;padding:9px 2px;
  border-bottom:1px solid var(--line);font-size:13.5px}
.nwrowitem a{color:var(--violet)}
.nwrowitem .nwt{flex:1;min-width:200px;color:var(--text)}
.nwstate{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;color:var(--muted-2);
  border:1px solid var(--line);border-radius:100px;padding:2px 9px}
.nwstate.on{color:var(--ok);border-color:rgba(52,211,153,.4)}
.nwdel{color:var(--danger);font-size:13px}

/* ---- admin tabs ---- */
.atabs{display:flex;gap:4px;flex-wrap:wrap;margin:0 0 24px;
  border-bottom:1px solid var(--line);padding-bottom:2px}
.atabs a{padding:9px 16px;font-size:14px;color:var(--muted);border-radius:var(--r) var(--r) 0 0;
  border:1px solid transparent;border-bottom:0;transition:.15s;display:flex;align-items:center;gap:8px}
.atabs a:hover{color:var(--text);background:var(--panel)}
.atabs a.on{color:var(--text);background:var(--panel);border-color:var(--line);
  box-shadow:0 1px 0 0 var(--panel-solid)}
.acount{font-size:11px;font-weight:700;line-height:1;padding:3px 7px;border-radius:100px;
  background:var(--gold);color:#2A1A00}

/* ---- news sections, formatting, media ---- */
.nwsect{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:8px}
.nwsect a{padding:7px 15px;border-radius:100px;font-size:13.5px;color:var(--muted);
  border:1px solid var(--line);transition:.15s}
.nwsect a:hover{color:var(--text);border-color:var(--line-2)}
.nwsect a.on{color:#fff;background:var(--violet-d);border-color:var(--violet-d);font-weight:600}
/* The Articles section: the card list beside a sticky "Содержание". */
.nw-layout{display:grid;grid-template-columns:minmax(0,1fr) 240px;gap:28px;
  align-items:start;max-width:1040px;margin:0 auto}
.nw-main{min-width:0}
.nw-layout .nwlist{margin:0}
.nw-toc{position:sticky;top:16px;background:var(--panel);border:1px solid var(--line);
  border-radius:14px;padding:15px 17px}
.nw-toc-h{font-size:12px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--muted-2);margin:0 0 10px}
.nw-toc-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  gap:9px;counter-reset:toc}
.nw-toc-list li{counter-increment:toc;display:flex;gap:8px}
.nw-toc-list li::before{content:counter(toc);color:var(--muted-2);font-size:12px;
  font-variant-numeric:tabular-nums;flex:0 0 auto;min-width:15px;text-align:right}
.nw-toc-list a{color:var(--read);font-size:13.5px;line-height:1.4}
.nw-toc-list a:hover{color:var(--violet)}
@media(max-width:860px){
  .nw-layout{grid-template-columns:1fr}
  .nw-toc{position:static}
}
.nwmeta{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0}
.nwtag{font-size:11px;letter-spacing:1.2px;text-transform:uppercase;color:var(--violet);
  border:1px solid var(--line);border-radius:100px;padding:2px 9px}
.nwfull h2{font-size:22px;font-weight:700;margin:28px 0 10px;letter-spacing:-.4px}
.nwfull blockquote{border-left:3px solid var(--violet-d);padding:2px 0 2px 16px;margin:0 0 16px;
  color:var(--muted);font-style:italic}
.nwfull ul{margin:0 0 16px 20px}
.nwfull li{font-size:16px;line-height:1.7;color:var(--read);margin-bottom:6px}
.nwfull a{color:var(--violet);text-decoration:underline}
.nwmedia{margin:0 0 18px}
/* max-height, not just width: a portrait photo at full column width comes out
   taller than the screen and pushes the rest of the article out of sight.
   width:auto with it, so capping the height narrows rather than squashes. */
.nwmedia img,.nwmedia video{max-width:100%;width:auto;height:auto;max-height:74vh;
  margin:0 auto;display:block;border-radius:var(--r-lg);
  border:1px solid var(--line);background:#0d0520}
/* A video that has not loaded yet has no intrinsic size and collapses to a
   sliver, so the article jumps when it arrives. Hold the usual shape. */
.nwmedia video{width:100%;aspect-ratio:16/9;object-fit:contain}
.nwmedia figcaption{font-size:12.5px;color:var(--muted-2);margin-top:7px;text-align:center}
.nwgal{margin-top:26px}
/* admin attachments */
.nwatt{border:1px solid var(--line);border-radius:var(--r-lg);padding:16px 18px;margin-bottom:16px;
  background:rgba(0,0,0,.14)}
.nwup{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.nwup input[type=file]{font-size:13px;color:var(--muted);flex:1;min-width:200px}
.nwatts{display:flex;gap:12px;flex-wrap:wrap;margin-top:14px}
.nwatt1{width:132px;text-align:center}
.nwatt1 img,.nwatt1 video{width:132px;height:88px;object-fit:cover;border-radius:var(--r);
  border:1px solid var(--line);display:block;background:#0d0520}
.nwatt1 .nwn{display:block;font-size:11px;color:var(--muted-2);margin-bottom:4px;
  font-family:var(--mono)}
.nwsyntax{margin-top:10px;font-size:12.5px;color:var(--muted-2)}
.nwsyntax summary{cursor:pointer;color:var(--violet)}
.nwsyntax ul{margin:8px 0 0 18px}
.nwsyntax li{margin-bottom:4px;font-family:var(--mono);font-size:12px}

/* ==========================================================================
   The formatting engine's output.
   ==========================================================================
   Every class here is written by prose/src/render.rs and nowhere else. The
   author's own colours, sizes and spacing arrive as inline style attributes
   generated from parsed numbers, so anything below is only the default — it
   must be specific enough to look right and loose enough to be overridden by
   a style attribute, which is to say: no !important, ever. */

.nwbody{margin-top:22px}
.pr-p{margin:0 0 16px;line-height:1.72;color:var(--ink)}
.pr-h{margin:30px 0 12px;line-height:1.3;color:var(--text)}
h2.pr-h{font-size:23px}
h3.pr-h{font-size:19px}
h4.pr-h{font-size:17px}
h5.pr-h{font-size:15px;color:var(--muted)}
.pr-list{margin:0 0 16px;padding-left:22px;line-height:1.72;color:var(--ink)}
.pr-list li{margin-bottom:6px}
.pr-quote{margin:0 0 18px;padding:2px 0 2px 16px;border-left:3px solid var(--violet);
  color:var(--muted);font-style:italic;line-height:1.7}
.pr-rule{margin:26px 0;border:0;border-top:1px solid var(--line)}
.pr-space{width:100%}
.nwbody code{font-family:var(--mono);font-size:.92em;padding:1px 5px;
  border-radius:var(--r-sm);background:rgba(0,0,0,.28);border:1px solid var(--line)}
.nwbody a{color:var(--violet)}

/* Attachments. max-height as well as max-width: a portrait photograph at full
   column width comes out taller than the screen and pushes the article out of
   sight. width:auto with it, so the cap narrows rather than squashes. */
.pr-media{margin:0 0 20px}
.pr-media img,.pr-media video{max-width:100%;width:auto;height:auto;max-height:74vh;
  display:block;margin:0 auto;border-radius:var(--r-lg);border:1px solid var(--line);
  background:#0d0520}
/* A video with no intrinsic size yet collapses to a sliver and makes the page
   jump when it arrives. Hold the usual shape until it loads. */
.pr-media video{width:100%;aspect-ratio:16/9;object-fit:contain}
.pr-media audio{width:100%;display:block;border-radius:var(--r);
  background:rgba(0,0,0,.25)}
.pr-media figcaption{font-size:12.5px;color:var(--muted-2);margin-top:8px;text-align:center}

/* Centred is the default — a block, image centred within it, and it starts a
   fresh line below any float above it. */
.pr-media-center{clear:both;margin-left:auto;margin-right:auto}

/* Left / right FLOAT, so text wraps beside the picture — which is the whole
   point of putting it on one side. The figure takes a fraction of the column
   (width= overrides the default, capping it) and the image fills the figure;
   the height cap is dropped because a tall image is fine with text alongside. */
.pr-media-left,.pr-media-right{width:46%}
.pr-media-left{float:left;margin:4px 26px 12px 0}
.pr-media-right{float:right;margin:4px 0 12px 26px}
.pr-media-left img,.pr-media-left video,
.pr-media-right img,.pr-media-right video{width:100%;max-height:none;margin:0}
.pr-media-left figcaption{text-align:left}
.pr-media-right figcaption{text-align:right}

/* A heading, a rule or a table starts a clean line below a float rather than
   riding up beside it, and the body contains its floats so whatever follows
   the article never rides up over one. A paragraph or a list still wraps —
   that is the point of the float. */
.pr-h,.pr-rule,.pr-tablewrap{clear:both}
.nwbody::after{content:"";display:block;clear:both}

/* On a phone a 46% float leaves text in a column too thin to read, so a
   left/right image just becomes a full-width block like a centred one. */
@media (max-width:640px){
  /* width:auto so a default float fills the width here; an explicit width= is
     an inline max-width and still caps it. Centred either way, so a narrow
     picture reads as deliberately small rather than stranded at the left. */
  .pr-media-left,.pr-media-right{float:none;width:auto;margin:0 auto 20px}
  .pr-media-left img,.pr-media-left video,
  .pr-media-right img,.pr-media-right video{max-height:74vh}
}

/* Inline media — [media:N] in a sentence or a table cell. On the line like a
   word; in a cell it becomes a block that fills the cell. */
.pr-inline{max-width:100%;height:auto;max-height:240px;vertical-align:middle;
  border-radius:var(--r);border:1px solid var(--line);background:#0d0520}
td .pr-inline,th .pr-inline{display:block;width:100%;max-height:none;margin:2px 0}

/* Tables. The wrapper scrolls rather than the page: a wide table must not put
   a horizontal scrollbar on the whole article. */
.pr-tablewrap{overflow-x:auto;margin:0 0 20px;-webkit-overflow-scrolling:touch}
.pr-table{border-collapse:collapse;width:100%;font-size:14.5px}
.pr-table th,.pr-table td{border:1px solid var(--line);padding:9px 13px;
  text-align:left;vertical-align:top}
.pr-table thead th{background:var(--panel-2);color:#fff;font-weight:600;
  white-space:nowrap}
.pr-table tbody tr:nth-child(even){background:rgba(255,255,255,.022)}

/* ---- the news editor ----------------------------------------------------
   Toolbar and preview are both built by editor.js. With JavaScript off the
   bar is an empty div with no children and collapses to nothing, and the
   preview pane is removed — the textarea grows to fill the row on its own. */
.nwed{margin-bottom:6px}
.nwed-bar{display:flex;flex-wrap:wrap;gap:5px;align-items:center}
.nwed-bar:not(:empty){margin-bottom:8px;padding:8px;border:1px solid var(--line);
  border-radius:var(--r);background:rgba(0,0,0,.16)}
.nwed-b{min-width:32px;height:30px;padding:0 8px;font-size:13px;cursor:pointer;
  color:var(--muted);background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-sm);line-height:1}
.nwed-b:hover{color:#fff;background:var(--panel-2);border-color:var(--violet)}
.nwed-sep{width:1px;height:20px;background:var(--line);margin:0 3px}
.nwed-sel select,.nwed-col{height:30px;font-size:12.5px;color:var(--muted);
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm)}
.nwed-sel select{padding:0 6px;max-width:118px}
.nwed-col{display:inline-flex;align-items:center;padding:0 4px;cursor:pointer}
.nwed-col input[type=color]{width:26px;height:20px;padding:0;border:0;background:none;
  cursor:pointer}

/* One column and no preview until the engine has actually loaded. With
   JavaScript off — or with the wasm missing — the textarea gets the whole row
   rather than sharing it with a pane that will never show anything. */
/* Stacked, not side by side: the write box on top and the preview below it,
   each the full width of the column. A news body is meant to be read at the
   article's real width, and half of it in a split pane is a different, narrower
   layout that lies about how the page will actually look. */
.nwed-panes{display:flex;flex-direction:column;gap:12px}
.nwed-preview{display:none}
.nwed.is-live .nwed-preview{display:block}
.nwed-src{font-family:var(--mono);font-size:13px;line-height:1.6;resize:vertical;
  min-height:300px}
.nwed-preview{border:1px solid var(--line);border-radius:var(--r);
  background:rgba(0,0,0,.16);padding:14px 16px;max-height:70vh;overflow-y:auto}
.nwed-label{font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted-2);margin:0 0 8px}
.nwed-preview .nwbody{margin-top:0}
.nwed-empty{color:var(--muted-2);font-size:13px;font-style:italic}

/* An attachment the body already places itself. */
.nwatt1.is-placed{opacity:.42}
.nwatt1.is-placed .nwn{color:var(--violet)}
.nwatt-au{display:flex;align-items:center;justify-content:center;width:132px;
  height:88px;font-size:30px;color:var(--violet);border-radius:var(--r);
  border:1px solid var(--line);background:#0d0520}

/* Fingers, not a mouse. Only on touch, so the bar does not gain a row on the
   desktop where most of this editing actually happens. */
@media (pointer:coarse){
  .nwed-b{height:36px;min-width:38px}
  .nwed-sel select,.nwed-col{height:36px}
}

/* ---- editor: popovers, number field, table grid -------------------------- */
.nwed-num{display:inline-flex;align-items:center;gap:3px;height:30px;padding:0 4px 0 7px;
  font-size:12.5px;color:var(--muted);background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-sm)}
.nwed-num .nwed-b{height:22px;min-width:22px;padding:0 5px}
.nwed-size{width:46px;height:22px;padding:0 2px;font-size:12.5px;text-align:center;
  color:var(--text);background:var(--field);border:1px solid var(--field-line);
  border-radius:var(--r-sm);-moz-appearance:textfield}
.nwed-unit{font-size:11px;color:var(--muted-2)}

.nwed-pop{margin:0 0 10px;padding:12px 14px;border:1px solid var(--violet);
  border-radius:var(--r);background:var(--panel-solid);box-shadow:0 8px 30px rgba(0,0,0,.45)}
.nwed-pop-t{margin:0 0 9px;font-size:12px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted-2)}
.nwed-pop-f{display:flex;align-items:center;gap:9px;margin-bottom:7px}
.nwed-pop-f>span{flex:0 0 92px;font-size:12.5px;color:var(--muted)}
.nwed-pop-f .input{flex:1;height:32px;font-size:13px}
.nwed-pop-go{display:flex;gap:8px;margin-top:10px}

.nwed-emoji{display:flex;flex-wrap:wrap;gap:2px;max-height:190px;overflow-y:auto}
.nwed-emoji button{width:34px;height:32px;font-size:18px;line-height:1;cursor:pointer;
  background:none;border:1px solid transparent;border-radius:var(--r-sm)}
.nwed-emoji button:hover{background:var(--panel-2);border-color:var(--violet)}

/* The table grid. Cells are real cells — merged ones span, and a pipe typed
   into one is just a character, because the engine escapes it on the way out. */
.nwed-tbl{margin:0 0 10px;padding:10px 12px;border:1px solid var(--violet);
  border-radius:var(--r);background:var(--panel-solid)}
.nwed-tbl-bar{display:flex;flex-wrap:wrap;gap:5px;align-items:center;margin-bottom:9px}
.nwed-tbl-t{font-size:12px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted-2);margin-right:5px}
.nwed-tbl-bar select{height:30px;padding:0 5px;font-size:12.5px;color:var(--muted);
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm)}
.nwed-tbl-h{margin:0 0 0 4px;font-size:12px}
.nwed-grid{border-collapse:collapse;width:100%;table-layout:fixed}
.nwed-grid td{border:1px solid var(--line);padding:0;vertical-align:top}
.nwed-grid td.is-sel{outline:2px solid var(--violet);outline-offset:-2px}
.nwed-grid tr.is-head td{background:var(--panel-2)}
.nwed-grid input{width:100%;height:32px;padding:0 7px;font-family:var(--mono);
  font-size:12.5px;color:#fff;background:none;border:0;outline:none}
.nwed-grid input:focus{background:rgba(167,139,250,.1)}

/* ---- editor: uploading -------------------------------------------------- */
.nwed{position:relative}
.nwed.is-drop::after{content:attr(data-drop);position:absolute;inset:0;z-index:5;
  display:flex;align-items:center;justify-content:center;pointer-events:none;
  font-size:14px;color:#fff;border:2px dashed var(--violet);border-radius:var(--r);
  background:rgba(124,58,237,.16)}
.nwed-say{margin:0 0 8px;padding:7px 11px;font-size:12.5px;border-radius:var(--r-sm);
  color:var(--violet);background:rgba(167,139,250,.1);border:1px solid var(--line)}
.nwed-say.is-bad{color:var(--warn);background:rgba(255,120,90,.09);
  border-color:rgba(255,120,90,.35)}
/* A picture in the preview is a thing you can click to change. */
.nwed-preview .pr-media{border-radius:var(--r);transition:outline-color .12s}
.nwed-preview .pr-media:hover{outline:2px solid var(--violet);outline-offset:3px}
.nwt-none{color:var(--muted-2);font-style:italic}

/* ---- admin: server log view --------------------------------------------- */
.logs-head{display:flex;align-items:baseline;gap:12px}
.logs-count{font-size:12px;color:var(--muted-2)}
.logs-filter{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}
.logs-filter a,.logs-filter button{font-size:12.5px;padding:5px 11px;border-radius:var(--r-sm);
  color:var(--muted);background:var(--panel);border:1px solid var(--line);text-decoration:none;
  cursor:pointer;font-family:inherit;line-height:1.4}
.logs-filter a:hover,.logs-filter button:hover{color:var(--text);border-color:var(--violet)}
.logs-filter a.on{color:var(--text);background:var(--panel-2);border-color:var(--violet)}
.logs-filter .logs-refresh{margin-left:auto}
.logs-filter #logs-copy{margin-left:6px}
.logs-refresh.spin{opacity:.5;pointer-events:none}
.offer-by{font-size:13.5px;color:var(--muted);margin:2px 0 6px}
.offer-by a{color:var(--violet)}
.logs{border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  font-family:var(--mono);font-size:12.5px;background:rgba(0,0,0,.22)}
.logline{display:grid;grid-template-columns:64px 52px 96px 1fr;gap:10px;
  padding:5px 12px;border-bottom:1px solid rgba(255,255,255,.04);align-items:baseline}
.logline:last-child{border-bottom:0}
.log-t{color:var(--muted-2)}
.log-l{text-transform:uppercase;font-size:10.5px;letter-spacing:.04em;font-weight:700}
.log-s{color:var(--violet);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.log-m{color:var(--read);white-space:pre-wrap;word-break:break-word}
.log-error .log-l{color:#ff6b6b}
.log-error{background:rgba(255,80,80,.06)}
.log-error .log-m{color:#ffd9d9}
.log-warn .log-l{color:var(--gold)}
.log-warn{background:rgba(255,200,80,.05)}
.log-info .log-l{color:#7fb0ff}
.log-debug .log-l,.log-trace .log-l{color:var(--muted-2)}
@media (max-width:640px){
  .logline{grid-template-columns:56px 44px;gap:6px}
  .log-s{grid-column:1 / -1;margin-top:2px}
  .log-m{grid-column:1 / -1}
}

/* admin: where an account has been — a list of label→value rows, not a strung
   -out line. The label column is only as wide as its widest label; the value
   fills the rest and wraps (the user agent is long and gets its own lines). */
.tracelist{display:grid;grid-template-columns:max-content 1fr;gap:3px 12px;
  margin:-4px 0 14px;font-size:12.5px;align-items:baseline}
.tracelist dt{color:var(--muted);font-weight:600}
.tracelist dd{margin:0;color:var(--muted-2);min-width:0;overflow-wrap:anywhere}
.tracelist-ua{font-family:var(--mono);font-size:11.5px;line-height:1.5}
@media (max-width:520px){
  /* Too narrow for two columns — label on its own line above its value. */
  .tracelist{grid-template-columns:1fr;gap:1px 0}
  .tracelist dt{margin-top:5px}
}

/* admin: a review author's nickname links to their account */
.cmt-author{text-decoration:none;color:inherit}
.cmt-author:hover{color:var(--violet)}
.cmt-author:hover b{text-decoration:underline}

/* admin-only trace banner on a public profile: a marked box with the same
   list inside, a header row carrying the tag and a link to the account. */
.adminbar{margin:0 0 20px;padding:11px 14px;font-size:12.5px;color:var(--muted);
  background:rgba(255,200,80,.06);border:1px solid rgba(255,200,80,.3);
  border-radius:var(--r)}
.adminbar-head{display:flex;align-items:center;gap:12px;margin-bottom:8px}
.adminbar-head a{margin-left:auto;color:var(--violet);text-decoration:none;white-space:nowrap}
.adminbar-head a:hover{text-decoration:underline}
.adminbar .tracelist{margin:0}
.adminbar-tag{font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;
  font-weight:700;color:var(--gold)}

/* ---- Tree of Life ---- */
.tree-layout{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:22px;align-items:start;margin-bottom:10px}
/* On a wide page the frame gets the extra room, so give the stage some height
   to match — otherwise the constellation sits in a letterbox that grew. */
body.wide .tree-svg{height:min(72vh,680px)}

/* The starfield stage: a REAL star map (static/starmap.svg — ~2900 bright stars
   from the HYG catalogue, projected equirectangular, coloured by spectral type,
   with a soft Milky Way band) under a gold bloom from above and a vignette that
   sinks the edges into the dark. */
.tree-frame{position:relative;padding:16px;border:1px solid rgba(246,232,166,.15);border-radius:22px;overflow:hidden;
  background:
    radial-gradient(120% 78% at 50% 4%, rgba(246,232,166,.06), transparent 50%),
    #06091a;
  box-shadow:inset 0 1px 0 rgba(246,232,166,.08), 0 26px 64px -30px rgba(0,0,0,.8)}
/* The starfield sits in its own layer so space can move: a slow ambient sway
   (the keyframes) plus a parallax offset (--star-x/y, nudged by tree.js as you
   pan the tree) that drifts it a little behind the constellation. Oversized so
   neither motion ever reveals an edge. */
.tree-frame::before{content:"";position:absolute;inset:-8%;z-index:0;pointer-events:none;
  background:url("/static/starmap.svg") center/cover no-repeat;
  background-position:calc(50% + var(--star-x,0px)) calc(50% + var(--star-y,0px));
  animation:tree-stars 90s ease-in-out infinite alternate;will-change:transform}
@keyframes tree-stars{
  from{transform:translate3d(-1.5%,-1%,0) scale(1.06)}
  to{transform:translate3d(1.6%,1.1%,0) scale(1.06)}}
@media(prefers-reduced-motion:reduce){.tree-frame::before{animation:none}}
/* Vignette above the stars, below the tree. */
.tree-frame::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(ellipse 82% 66% at 50% 48%, transparent 40%, rgba(4,6,18,.78) 98%)}
.tree-svg{display:block;width:100%;height:min(64vh,560px);
  cursor:grab;touch-action:none;position:relative;z-index:2}
.tree-svg.grabbing{cursor:grabbing}

/* Paths: thin gold threads, low until their knot is chosen. */
.tpath{stroke:#f6e8a6;stroke-width:1.1;opacity:.15;transition:opacity .3s,stroke-width .3s}
.tpath.active{opacity:.6;stroke-width:1.6}

/* Knots: glowing golden orbs (fill from the SVG radial gradient), a soft halo
   always faintly lit and brighter when hovered or open, a bright rim, dark ink
   numeral. */
.tnode{cursor:pointer;outline:none}
.tnode-halo{fill:#f6e8a6;opacity:.13;transition:opacity .3s}
.tnode:hover .tnode-halo,.tnode:focus-visible .tnode-halo,.tnode.active .tnode-halo{opacity:.36}
.tnode-orb{fill:url(#seph-orb);transition:filter .3s}
.tnode:hover .tnode-orb,.tnode:focus-visible .tnode-orb,.tnode.active .tnode-orb{filter:drop-shadow(0 0 7px rgba(246,232,166,.9))}
.tnode-sheen{fill:none;stroke:rgba(255,250,222,.55);stroke-width:1;opacity:.45;pointer-events:none}
.tnode.active .tnode-sheen{opacity:.85}
.tnode-num{fill:#3a2c07;font-size:12px;font-weight:700;pointer-events:none;
  -webkit-user-select:none;user-select:none}
/* The sephirah the day belongs to: lit before anything is clicked, so the page
   opens already knowing what today is about. It breathes, slowly. */
.tnode--today .tnode-halo{opacity:.4}
.tnode--today .tnode-orb{filter:drop-shadow(0 0 8px rgba(246,232,166,.9))}
.tnode-today-ring{fill:none;stroke:#fff6c6;stroke-width:1.1;opacity:.55;
  animation:seph-breathe 5.5s ease-in-out infinite}
@keyframes seph-breathe{0%,100%{opacity:.3;r:20}50%{opacity:.7;r:23}}
@media(prefers-reduced-motion:reduce){.tnode-today-ring{animation:none}}
/* A day practiced here leaves a mark. Over a season these are the member's own
   year, drawn on the tree — never a score, just a trace. */
.tnode-visit{fill:none;stroke:#f6e8a6;stroke-width:1.4;opacity:.34;pointer-events:none}

.tree-zoombar{position:absolute;top:12px;right:12px;z-index:3;display:flex;flex-direction:column;gap:6px}
.tzoom-btn{width:30px;height:30px;border-radius:8px;border:1px solid rgba(246,232,166,.2);background:rgba(10,14,39,.6);
  color:#f6e8a6;font-size:16px;line-height:1;cursor:pointer;display:flex;align-items:center;
  justify-content:center;transition:.15s}
.tzoom-btn:hover{border-color:#f6e8a6;color:#fff6c6}
.tree-tip{position:fixed;z-index:60;max-width:240px;pointer-events:none;
  background:linear-gradient(rgba(10,14,39,.96),rgba(10,14,39,.96)),var(--bg);
  border:1px solid rgba(246,232,166,.3);border-radius:10px;padding:9px 12px;
  box-shadow:0 14px 34px -14px rgba(0,0,0,.8)}
.tree-tip-h{font-size:13px;font-weight:700;color:#fff6c6;margin-bottom:3px}
.tree-tip-d{font-size:12.5px;color:var(--read);line-height:1.45}
.tree-cap{text-align:center;color:var(--muted-2);font-size:12.5px;margin-top:10px;
  letter-spacing:.03em;font-style:italic}
.tree-zoomhint{opacity:.7}

/* The tree's tooltip and panel paint their own near-opaque navy, so they stay
   DARK in the light theme while the reading tokens flip to near-black — which
   rendered the contemplation body, the daily lines and the reflection box as
   dark-on-dark (contrast 1.1). Pin the two text tokens back to the base palette
   inside those two surfaces; the gold labels are literals already and read
   correctly either way. `--muted`/`--muted-2` are deliberately NOT pinned: the
   light theme's mid-purple greys read BETTER on this navy than the base ones. */
.tree-tip,.seph-panel{--text:#EDE9FE;--read:#E4DFF7}

/* ---- the click-to-open sephirah panel ---- */
.seph-panel{position:relative;overflow:hidden;
  background:linear-gradient(rgba(10,14,39,.92),rgba(10,14,39,.92)),var(--bg);
  border:1px solid rgba(246,232,166,.16);border-radius:20px;padding:22px 20px 14px;
  box-shadow:inset 0 1px 0 rgba(246,232,166,.08), 0 24px 60px -30px rgba(0,0,0,.7)}
.seph-detail[hidden]{display:none}   /* author display must not defeat `hidden` */
.seph-close{position:absolute;top:12px;right:12px;width:28px;height:28px;border-radius:50%;
  border:1px solid rgba(246,232,166,.22);background:transparent;color:rgba(246,232,166,.75);
  font-size:14px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s}
.seph-close:hover{border-color:#f6e8a6;color:#fff6c6}
.seph-eyebrow{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:rgba(246,232,166,.55);
  margin:0 0 6px;padding-right:26px}
.seph-heb{font-size:15px;letter-spacing:0;color:rgba(246,232,166,.85)}
.seph-name{font-family:Georgia,"Times New Roman",serif;font-weight:400;font-size:27px;letter-spacing:.06em;
  color:#fff6c6;line-height:1.1;margin:0 0 2px}
.seph-tr{font-size:13.5px;color:rgba(246,232,166,.7);margin:0 0 14px}
.seph-pillar{display:flex;align-items:center;justify-content:space-between;gap:10px;
  border:1px solid rgba(246,232,166,.15);border-radius:12px;padding:9px 12px;margin:0 0 16px;
  background:rgba(246,232,166,.04)}
.seph-pillar-l{display:block;font-size:10px;letter-spacing:.15em;text-transform:uppercase;color:rgba(246,232,166,.5)}
.seph-pillar-v{display:block;font-size:14px;color:#f6e8a6;letter-spacing:.04em}
.seph-pillar-badge{display:inline-flex;gap:3px;align-items:center}
.seph-pillar-badge i{width:2px;height:13px;background:rgba(246,232,166,.28);border-radius:1px}
.seph-pillar--left .seph-pillar-badge i:nth-child(1),
.seph-pillar--mid .seph-pillar-badge i:nth-child(2),
.seph-pillar--right .seph-pillar-badge i:nth-child(3){background:#f6e8a6;height:18px}
.seph-sec-l,.seph-medit-l{font-size:10px;letter-spacing:.15em;text-transform:uppercase;
  color:rgba(246,232,166,.5);margin:0 0 6px}
.seph-body{font-size:13.5px;line-height:1.65;color:var(--read)}
.seph-body p{margin:0 0 8px}
.seph-medit{margin-top:16px;border:1px solid rgba(246,232,166,.15);border-radius:12px;padding:12px 14px;
  background:rgba(246,232,166,.03)}
.seph-medit-l{display:flex;align-items:center;justify-content:space-between;gap:8px}
.seph-kav{font-size:13px;letter-spacing:0;color:rgba(246,232,166,.7);text-transform:none}
.seph-medit-body{font-size:13px;line-height:1.6;color:rgba(246,232,166,.85);font-style:italic}
.seph-medit-body p{margin:0}
.seph-foot{margin:16px 0 0;padding-top:12px;border-top:1px solid rgba(246,232,166,.12);
  text-align:center;font-size:11px;letter-spacing:.28em;color:rgba(246,232,166,.45)}

/* ---- the daily contemplation ---- */
/* Lives inside today's sephirah panel: the day's name, the card of the day, the
   question to carry, one optional line, and the close. Nothing here counts
   down — the only number is the rhythm line on the frame, which never falls. */
.today{margin-top:16px;padding-top:15px;border-top:1px solid rgba(246,232,166,.14)}
.today-day{margin:0 0 12px;font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:rgba(246,232,166,.6)}
.today-lead{margin:0 0 11px;font-size:13px;line-height:1.5;color:var(--read)}
.today-invite{display:flex;gap:14px;align-items:center}
.today-back{flex:0 0 auto;width:74px;height:110px;border-radius:11px;display:grid;place-items:center;
  border:1px solid rgba(246,232,166,.34);color:rgba(246,232,166,.7);
  background:radial-gradient(120% 90% at 50% 22%, rgba(246,232,166,.14), transparent 62%),#0b1030;
  box-shadow:0 10px 26px -14px rgba(0,0,0,.8)}
.today-reveal{display:flex;gap:15px;align-items:flex-start}
.today-card{position:relative;flex:0 0 auto;width:92px;height:138px;border-radius:11px;overflow:hidden;
  display:grid;place-items:center;border:1px solid rgba(246,232,166,.42);
  background:linear-gradient(180deg,#161d42,#0a0f2c);
  box-shadow:0 14px 32px -14px rgba(0,0,0,.85), inset 0 0 30px rgba(246,232,166,.05)}
.today-card img{width:100%;height:100%;object-fit:cover;display:block}
.today-card-glyph{font-size:34px;color:rgba(246,232,166,.85)}
.today-card-num{position:absolute;top:7px;left:0;right:0;text-align:center;font-size:10.5px;
  letter-spacing:.16em;color:rgba(246,232,166,.75);text-shadow:0 1px 3px rgba(0,0,0,.9)}
.today-card-name{position:absolute;bottom:6px;left:0;right:0;text-align:center;font-size:12.5px;
  color:#fff6c6;text-shadow:0 1px 4px rgba(0,0,0,.95)}
.today-meaning{margin:0;font-size:13px;line-height:1.6;color:var(--read)}
.today-q{margin-top:16px;border:1px solid rgba(246,232,166,.16);border-radius:12px;padding:12px 14px;
  background:rgba(246,232,166,.035)}
.today-q-text{margin:3px 0 0;font-size:16.5px;line-height:1.4;color:#fff6c6}
.today-reflect{margin-top:14px}
.today-line{width:100%;resize:vertical;min-height:44px;max-height:150px;border-radius:10px;padding:10px 12px;
  font:inherit;font-size:13px;line-height:1.5;color:var(--text);background:rgba(255,255,255,.015);
  border:1px solid rgba(246,232,166,.14)}
.today-line:focus{outline:none;border-color:rgba(246,232,166,.4)}
.today-reflect-row{display:flex;align-items:center;gap:10px;margin-top:8px}
.today-kept{font-size:11.5px;color:rgba(246,232,166,.55)}
.today-done{margin:15px 0 0;text-align:center;font-size:12px;color:rgba(246,232,166,.5);font-style:italic}
/* The rhythm line on the frame — contemplations this moon. */
.tree-rhythm{position:absolute;top:14px;left:16px;z-index:3;margin:0;font-size:11px;
  letter-spacing:.05em;color:rgba(246,232,166,.5)}

/* ---- choosing the card: ten veils over the tree ---- */
.pick[hidden]{display:none}
.pick{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:20px}
.pick-scrim{position:absolute;inset:0;background:rgba(4,6,18,.86);backdrop-filter:blur(3px)}
.pick-box{position:relative;max-width:min(94vw,720px);text-align:center;padding:8px 10px}
.pick-kick{margin:0 0 8px;font-size:11px;letter-spacing:.2em;text-transform:uppercase;
  color:rgba(246,232,166,.55)}
.pick-title{margin:0;font-size:clamp(21px,4vw,30px);font-weight:500;color:#fff6c6;letter-spacing:.03em}
.pick-sub{margin:9px 0 26px;font-size:13.5px;color:var(--read)}
.pick-fan{display:flex;flex-wrap:wrap;gap:9px;justify-content:center}
.pick-fan form{margin:0}
.pick-veil{width:66px;height:100px;border-radius:10px;cursor:pointer;display:grid;place-items:center;
  border:1px solid rgba(246,232,166,.3);color:rgba(246,232,166,.55);
  background:radial-gradient(120% 90% at 50% 22%, rgba(246,232,166,.12), transparent 62%),#0b1030;
  box-shadow:0 12px 26px -14px rgba(0,0,0,.85);transition:transform .18s,border-color .18s,box-shadow .18s,color .18s}
.pick-veil:hover,.pick-veil:focus-visible{transform:translateY(-14px);outline:none;
  border-color:rgba(246,232,166,.75);color:#fff6c6;
  box-shadow:0 0 22px rgba(246,232,166,.28), 0 16px 30px -14px rgba(0,0,0,.9)}
.pick-hint{margin:24px 0 0;font-size:12px;color:rgba(215,208,234,.45);font-style:italic}
.pick-x{position:absolute;top:-6px;right:-2px;width:30px;height:30px;border-radius:50%;cursor:pointer;
  border:1px solid rgba(246,232,166,.22);background:transparent;color:rgba(246,232,166,.75);font-size:14px}
.pick-x:hover{border-color:#f6e8a6;color:#fff6c6}
@media(max-width:520px){.pick-veil{width:56px;height:86px}}

.seph-hint{display:none;color:var(--muted-2);text-align:center;padding:34px 12px;font-size:13.5px;line-height:1.6}
.seph-panel.empty .seph-detail{display:none}
.seph-panel.empty .seph-hint{display:block}
.seph-panel.empty .seph-close{display:none}
@media(max-width:820px){.tree-layout{grid-template-columns:1fr}}
.tree-cards{margin:34px 0 22px}
.tslots{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(92px,1fr))}
.tslot{position:relative;aspect-ratio:2/3;border:1px dashed var(--line);border-radius:12px;
  background:var(--panel-2);display:flex;align-items:center;justify-content:center;cursor:pointer;
  outline:none;transition:transform .2s,border-color .2s,box-shadow .2s,background .2s}
.tslot:hover,.tslot:focus-visible{transform:translateY(-6px);border-color:var(--violet);
  border-style:solid;background:rgba(124,58,237,.10);box-shadow:0 16px 34px -18px rgba(124,58,237,.85)}
.tslot-n{position:absolute;top:7px;left:9px;font-size:11px;color:var(--muted-2);letter-spacing:.05em}
.tslot-mark{font-size:22px;color:var(--muted-2);opacity:.4;transition:transform .2s,color .2s,opacity .2s}
.tslot:hover .tslot-mark,.tslot:focus-visible .tslot-mark{color:var(--gold);opacity:1;transform:scale(1.2)}

/* ---- Kabbalah section ---- */
/* The section name, small and quiet above each subtab's title. */
.kab-eyebrow{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold);margin-bottom:6px}
/* The subtabs — the same pill row the news sections use. */
.kabtabs{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin:0 0 18px}
.kabtabs a{padding:7px 15px;border-radius:100px;font-size:13.5px;color:var(--muted);
  border:1px solid var(--line);transition:.15s}
.kabtabs a:hover{color:var(--text);border-color:var(--line-2)}
.kabtabs a.on{color:#fff;background:var(--violet-d);border-color:var(--violet-d);font-weight:600}

/* The Qliphoth is the Tree of Life inverted — the same frame, geometry and
   golden-orb structure, drawn in shadow: crimson and ash where the tree is gold.
   Only skin-deep; the markup, the paths and tree.js are shared. The orbs pick the
   red gradient (#seph-orb-q) by re-pointing their fill. */
.qtree{border-color:rgba(224,86,74,.2);
  background:
    radial-gradient(120% 78% at 50% 4%, rgba(224,86,74,.10), transparent 50%),
    #0a0406}
/* Same drifting starfield (shared ::before), tinted crimson by the wash + a
   darker vignette on ::after. */
.qtree.tree-frame::after{background:
  linear-gradient(rgba(34,7,11,.5), rgba(20,4,8,.56)),
  radial-gradient(ellipse 82% 66% at 50% 48%, transparent 40%, rgba(7,3,10,.8) 98%)}
.qtree .tpath{stroke:#e0564a}
.qtree .tnode-halo{fill:#e0564a}
.qtree .tnode-orb{fill:url(#seph-orb-q)}
.qtree .tnode:hover .tnode-orb,.qtree .tnode:focus-visible .tnode-orb,.qtree .tnode.active .tnode-orb{filter:drop-shadow(0 0 7px rgba(224,86,74,.9))}
.qtree .tnode-num{fill:#2a0808}
.qtree .tnode-sheen{stroke:rgba(255,220,207,.5)}
.qtree .tzoom-btn{color:#e0564a;border-color:rgba(224,86,74,.24)}
.qtree .tzoom-btn:hover{border-color:#e0564a;color:#ff9a8f}
.qtree .tree-tip{border-color:rgba(224,86,74,.35)}
.qtree .tree-tip-h{color:#ff9a8f}
/* The shells' detail panel answers in crimson too. */
.qlayout .seph-panel{border-color:rgba(224,86,74,.18)}
.qlayout .seph-panel .seph-heb,.qlayout .seph-panel .seph-tr,.qlayout .seph-panel .seph-pillar-v,.qlayout .seph-panel .seph-kav{color:#ff9a8f}
.qlayout .seph-panel .seph-name{color:#ffd9d2}
.qlayout .seph-panel .seph-eyebrow,.qlayout .seph-panel .seph-sec-l,.qlayout .seph-panel .seph-medit-l,.qlayout .seph-panel .seph-pillar-l{color:rgba(224,86,74,.6)}
.qlayout .seph-panel .seph-pillar,.qlayout .seph-panel .seph-medit{border-color:rgba(224,86,74,.16);background:rgba(224,86,74,.04)}
.qlayout .seph-panel .seph-pillar-badge i{background:rgba(224,86,74,.28)}
.qlayout .seph-panel .seph-pillar--left .seph-pillar-badge i:nth-child(1),
.qlayout .seph-panel .seph-pillar--mid .seph-pillar-badge i:nth-child(2),
.qlayout .seph-panel .seph-pillar--right .seph-pillar-badge i:nth-child(3){background:#e0564a}
.qlayout .seph-panel .seph-close{color:rgba(224,86,74,.75);border-color:rgba(224,86,74,.22)}
.qlayout .seph-panel .seph-medit-body{color:#ffd9d2}
/* The shells (Qliphoth card slots) answer in crimson too. */
.qslots .tslot:hover,.qslots .tslot:focus-visible{border-color:#c0392b}
.qslots .tslot:hover .tslot-mark,.qslots .tslot:focus-visible .tslot-mark{color:#e0564a}

/* Admin-written prose on the section pages: an editable intro in the hero, and
   the block under a tree. Both reuse .nwbody for the prose styling itself. */
.dhero-intro{max-width:62ch;margin:12px auto 0;text-align:left}
.kab-under{max-width:760px;margin:26px auto 0}

/* The Kabbalah content tab: the block list and the editor it opens. */
.kabgrid{display:flex;flex-direction:column;gap:8px;margin-bottom:6px}
.kabrow{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding:10px 13px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel)}
.kabrow-name{flex:1;min-width:180px;font-size:14px;color:#fff}
.kabrow-langs{display:flex;gap:6px;flex-wrap:wrap}
.kabedit{margin:0}
.kabedit .kabfilled{border-color:var(--violet);color:#fff}
.kab-editing{font-size:13px;color:var(--gold);margin:0 0 10px;letter-spacing:.02em}
.kabeditor{margin-top:16px;border-top:1px solid var(--line);padding-top:16px}

/* ---- floating chat widget (VK/Facebook style) ---- */
#chatw{position:fixed;right:18px;bottom:18px;z-index:120}
.cw-bubble{position:relative;width:40px;height:40px;border-radius:50%;border:1px solid var(--line);
  background:linear-gradient(160deg,var(--violet),#5b2bd6);color:#fff;font-size:17px;cursor:pointer;
  box-shadow:0 7px 22px -8px rgba(124,58,237,.7);display:grid;place-items:center;transition:.15s}
.cw-bubble:hover{transform:translateY(-2px)}
.cw-bubble.on{background:var(--panel-2)}
.cw-badge{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 4px;
  border-radius:9px;background:var(--danger);color:#fff;font-size:10.5px;font-weight:700;
  display:grid;place-items:center;border:2px solid var(--bg)}
/* Author display:grid defeats the `hidden` attribute (same trap as the panel), so
   without this the red circle shows even at 0 unread. Only appears with a count. */
.cw-badge[hidden]{display:none}
/* Opaque: the (nearly transparent) panel tint as a solid gradient layer over the
   solid page background — `background: <color>, <color>` is invalid and would be
   dropped (leaving it fully see-through), so the tint has to be an image layer.
   Theme-aware, opaque in both. */
.cw-panel{position:absolute;right:0;bottom:70px;width:min(92vw,360px);height:min(70vh,520px);
  display:flex;flex-direction:column;
  background:linear-gradient(var(--panel),var(--panel)),var(--bg);border:1px solid var(--line);
  border-radius:16px;overflow:hidden;box-shadow:0 24px 60px -18px rgba(0,0,0,.7)}
/* An author `display:flex` beats the UA `[hidden]{display:none}`, so every part
   of the widget that sets `display:flex` is NEVER actually hidden by its `hidden`
   attribute. Three consequences, each a real bug:
     • .cw-panel    — sits open at the default corner; the bubble toggle looks
                      dead and applyGeom (runs only on open) never restores geom.
     • .cw-thread   — the empty thread pane keeps its flex:1 slot beside the list,
                      so the conversation list is squeezed to ~half width and each
                      row's name collapses to a few px ("half a letter, then the
                      date"). This one bit hardest.
     • .cw-composer — the message bar shows in the list view where it shouldn't.
   Restoring `display:none` when `[hidden]` is the whole fix. */
.cw-panel[hidden],.cw-thread[hidden],.cw-composer[hidden]{display:none}
/* Slim title bar — it also carries the drag. Opaque like the panel. */
.cw-head{display:flex;align-items:center;gap:6px;padding:2px 8px;border-bottom:1px solid var(--line);
  background:linear-gradient(var(--panel-2),var(--panel-2)),var(--bg);
  cursor:grab;user-select:none;touch-action:none}
.cw-head.cw-dragging{cursor:grabbing}
.cw-head .cw-title{flex:1;font-weight:700;font-size:12.5px;color:var(--text);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.cw-back,.cw-x,.cw-full{background:none;border:0;color:var(--muted);cursor:pointer;font-size:13px;
  line-height:1;padding:2px 4px;text-decoration:none}
/* The back arrow is the prominent control of the thread view. */
.cw-back{font-size:30px;line-height:1;padding:0 4px}
.cw-back:hover,.cw-x:hover,.cw-full:hover{color:var(--text)}
/* Header controls sit above the resize strips so they stay clickable near the
   corners. */
.cw-head button,.cw-head .cw-full{position:relative;z-index:6}
/* Resize from every edge and corner: thin strips on the sides, small squares in
   the corners. Hidden on phones, where the panel is near full-width. */
.cw-rz{position:absolute;z-index:4;touch-action:none}
.cw-rz-n{top:0;left:12px;right:12px;height:6px;cursor:ns-resize}
.cw-rz-s{bottom:0;left:12px;right:12px;height:6px;cursor:ns-resize}
.cw-rz-e{right:0;top:12px;bottom:12px;width:6px;cursor:ew-resize}
.cw-rz-w{left:0;top:12px;bottom:12px;width:6px;cursor:ew-resize}
.cw-rz-ne{top:0;right:0;width:12px;height:12px;cursor:nesw-resize}
.cw-rz-nw{top:0;left:0;width:12px;height:12px;cursor:nwse-resize}
.cw-rz-se{bottom:0;right:0;width:12px;height:12px;cursor:nwse-resize}
.cw-rz-sw{bottom:0;left:0;width:12px;height:12px;cursor:nesw-resize}
@media(max-width:520px){.cw-rz{display:none}}
/* The "You:" sender tag in a conversation preview. */
.cw-you{font-style:normal;color:var(--muted-2)}
.cw-body{flex:1;min-height:0;overflow:hidden;display:flex}
.cw-list{flex:1;overflow-y:auto;padding:6px}
.cw-thread{flex:1;overflow-y:auto;padding:9px;display:flex;flex-direction:column;gap:5px}
.cw-empty{color:var(--muted-2);text-align:center;font-size:13px;padding:24px 12px}
.cw-conv{display:flex;gap:10px;align-items:center;width:100%;text-align:left;background:none;
  border:0;border-radius:10px;padding:5px 7px;cursor:pointer;color:inherit}
.cw-conv:hover{background:var(--panel-2)}
.cw-av{flex:0 0 auto;width:40px;height:40px;border-radius:50%;overflow:hidden;display:grid;
  place-items:center;color:#fff;font-weight:700;font-size:14px}
.cw-av img{width:100%;height:100%;object-fit:cover}
.cw-conv-main{flex:1;min-width:0}
.cw-conv-top{display:flex;justify-content:space-between;gap:8px;align-items:baseline}
.cw-conv-top b{font-size:13.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cw-conv-top time{font-size:11px;color:var(--muted-2);flex:0 0 auto}
.cw-conv-prev{display:block;font-size:12.5px;color:var(--muted);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.cw-conv.unread .cw-conv-prev,.cw-conv.unread b{color:var(--text)}
.cw-dot{width:8px;height:8px;border-radius:50%;background:var(--violet);flex:0 0 auto}
.cw-msg{max-width:80%;align-self:flex-start;background:var(--panel-2);border:1px solid var(--line);
  border-radius:12px;padding:5px 9px;font-size:13.5px;line-height:1.35}
.cw-msg.mine{align-self:flex-end;background:rgba(124,58,237,.22);border-color:rgba(124,58,237,.4)}
.cw-msg-b{color:var(--text);white-space:pre-wrap;overflow-wrap:anywhere}
.cw-msg-at{display:block;font-size:10.5px;color:var(--muted-2);margin-top:1px}
/* Same tombstone as the full thread, at widget scale. */
.cw-msg.gone{background:transparent;border-style:dashed}
.cw-gone{color:var(--muted-2);font-style:italic}
.cw-media{display:block;max-width:100%;border-radius:8px;margin-bottom:4px}
.cw-media img,video.cw-media{max-width:100%;border-radius:8px;display:block}
.cw-audio{width:100%;margin-bottom:4px}
/* Compact message bar: ~30% smaller than before, with a smaller send button. */
.cw-composer{display:flex;gap:5px;align-items:flex-end;padding:5px 6px;border-top:1px solid var(--line);
  background:var(--panel-2)}
.cw-attach{cursor:pointer;font-size:15px;padding:4px;color:var(--muted);flex:0 0 auto}
.cw-attach:hover{color:var(--text)}
.cw-composer.has-file .cw-attach{color:var(--gold)}
/* One line by default; JS grows it to a second line when the message wraps and
   shrinks it back when it fits again. max-height is the two-line ceiling. */
.cw-composer textarea{flex:1;min-width:0;resize:none;overflow-y:auto;max-height:46px;
  background:var(--panel);border:1px solid var(--line);border-radius:8px;color:var(--text);
  padding:5px 8px;font:inherit;font-size:12.5px;line-height:1.35}
.cw-send{flex:0 0 auto;background:var(--violet);color:#fff;border:0;border-radius:8px;
  padding:5px 10px;font-size:12px;cursor:pointer}
.cw-send:hover{filter:brightness(1.1)}
@media(max-width:520px){
  #chatw{right:12px;bottom:12px}
  .cw-panel{width:calc(100vw - 24px);height:min(76vh,560px)}
}

/* ---- the path so far: the member's own diary of contemplations ---- */
/* Read from the top, never graded. A gap between dates is a day lived
   elsewhere, not a failure, so nothing here marks one. */
.path-count{text-align:center;color:rgba(246,232,166,.6);font-size:12.5px;letter-spacing:.06em;
  margin:0 0 20px}
.pathnote{text-align:center;color:var(--muted);font-size:13.5px;line-height:1.6;margin:22px 0}
.pathback{text-align:center;margin:26px 0 10px;font-size:13px}
.pathlist{list-style:none;margin:0 auto;padding:0;max-width:720px;display:flex;flex-direction:column;gap:10px}
.pathrow{display:flex;gap:14px;align-items:flex-start;padding:12px 14px;border-radius:14px;
  border:1px solid rgba(246,232,166,.12);background:rgba(246,232,166,.025)}
.pathrow-card{flex:0 0 auto;width:52px;height:78px;border-radius:8px;overflow:hidden;display:grid;
  place-items:center;border:1px solid rgba(246,232,166,.28);
  background:linear-gradient(180deg,#161d42,#0a0f2c)}
.pathrow-card img{width:100%;height:100%;object-fit:cover;display:block}
.pathrow-glyph{font-size:22px;color:rgba(246,232,166,.8)}
.pathrow-main{flex:1;min-width:0}
.pathrow-top{display:flex;justify-content:space-between;gap:10px;align-items:baseline}
.pathrow-top b{font-size:15px;color:#fff6c6;font-weight:500;letter-spacing:.03em}
.pathrow-top time{font-size:11.5px;color:var(--muted-2);flex:0 0 auto}
.pathrow-card-name{display:block;font-size:12.5px;color:rgba(246,232,166,.7);margin-top:2px}
.pathrow-body{display:block;margin-top:7px;font-size:13px;line-height:1.55;color:var(--read);
  white-space:pre-wrap;overflow-wrap:anywhere}
.today-path{margin:9px 0 0;text-align:center;font-size:12.5px}
@media(max-width:520px){.pathrow{padding:10px 11px;gap:11px}}

/* ---- admin: editing one member ---- */
/* Each act is its own panel and its own form: renaming somebody, resetting
   their password and rewriting their diary are separate decisions and must not
   be one mis-click apart. */
.aedit{background:var(--panel);border:1px solid var(--line);border-radius:16px;
  padding:18px 20px;margin-bottom:14px}
.aedit h2{font-size:16.5px;margin-bottom:12px}
.aform{max-width:460px}
.nwrow{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px}
.okline{color:var(--ok,#4ade80);font-size:13px;margin:0 0 14px}
.noedit{font-size:12.8px;color:var(--warn,#eab308)}
.uedit-link{margin:10px 0 0;font-size:13px}
.aavatar{display:flex;gap:16px;align-items:center;flex-wrap:wrap}
.aavatar img{width:88px;height:88px;border-radius:50%;object-fit:cover;
  border:1px solid var(--line-2)}
.aavatar-none{font-size:13px;color:var(--muted-2)}
.aavatar-acts{display:flex;flex-direction:column;gap:8px}
.aavatar-acts form{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin:0}
/* The member's diary, one form per day. */
.ajournal{display:flex;flex-direction:column;gap:10px}
.ajrow{border:1px solid var(--line);border-radius:12px;padding:11px 13px;
  background:rgba(0,0,0,.14)}
.ajtop{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:8px}
.ajtop time{font-size:12.5px;color:var(--muted-2);min-width:82px}
.ajpick{margin:0}
.ajpick .input{padding:5px 8px;font-size:12.5px}
.ajpick select.input{max-width:190px}
.ajpick input.input{width:82px}
.ajcard{font-size:12.5px;color:rgba(246,232,166,.75)}
.ajacts{display:flex;gap:12px;align-items:center;margin-top:8px}
@media(max-width:520px){.ajtop{gap:7px}.ajpick select.input{max-width:150px}}

/* The path's richer rows: the day's sky, what the card said, and the question
   carried — so reading back is remembering, not just a list of dates. */
.pathrow-when{display:block;font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;
  color:rgba(246,232,166,.45);margin-top:3px}
.pathrow-q{display:block;margin-top:7px;font-size:13.5px;line-height:1.45;color:#fff6c6}
.pathrow-card-name i{font-style:italic;color:var(--muted)}
/* A moon day: rarer than a weekday, and the only thing that lights the crown,
   the spark or the world. */
.today-moon{margin:-8px 0 12px;font-size:12px;letter-spacing:.1em;color:#fff6c6}

/* ---- quick theme switch (app bar) ---- */
/* The collapsed icon names the theme IN EFFECT, which CSS decides — the server
   stamps nothing at all for "follow the device", so only the browser knows
   whether that means light or dark. Each button already carries the theme it
   switches TO, so a plain click is the swap and no script is involved. */
.thmsw{position:relative;display:inline-flex}
.thmsw-quick{display:inline-flex;margin:0}
.thmsw-btn{display:none;align-items:center;justify-content:center;width:30px;height:30px;
  padding:0;border:1px solid var(--line);border-radius:9px;background:transparent;
  color:var(--muted);cursor:pointer;transition:.15s}
.thmsw-btn:hover,.thmsw-btn:focus-visible{color:var(--text);border-color:var(--line-2)}
/* Dark is the default surface, so the moon is what shows unless light is on. */
.thmsw-moon{display:inline-flex}
:root[data-theme="light"] .thmsw-moon{display:none}
:root[data-theme="light"] .thmsw-sun{display:inline-flex}
@media(prefers-color-scheme:light){
  :root:not([data-theme="dark"]) .thmsw-moon{display:none}
  :root:not([data-theme="dark"]) .thmsw-sun{display:inline-flex}
}
/* Both choices, on hover or keyboard focus. */
.thmsw-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:60;min-width:150px;
  display:none;flex-direction:column;gap:2px;padding:5px;border-radius:11px;
  border:1px solid var(--line);
  background:linear-gradient(var(--panel),var(--panel)),var(--bg);
  box-shadow:0 16px 38px -18px rgba(0,0,0,.75)}
.thmsw:hover .thmsw-menu,.thmsw:focus-within .thmsw-menu{display:flex}
.thmsw-menu form{margin:0}
.thmsw-opt{display:flex;align-items:center;gap:9px;width:100%;padding:7px 9px;border:0;
  border-radius:8px;background:transparent;color:var(--read);font:inherit;font-size:13px;
  text-align:left;cursor:pointer}
.thmsw-opt:hover,.thmsw-opt:focus-visible{background:var(--panel-2);color:var(--text);outline:none}
/* A hover menu is a mouse idea; on touch the icon alone still swaps the theme. */
@media(hover:none){.thmsw-menu{display:none!important}}

/* ---- own settings: picture, email, password ---- */
.setrow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.setrow form{margin:0;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.setav img{width:46px;height:46px;border-radius:50%;object-fit:cover;border:1px solid var(--line-2);display:block}
.setav-none{font-size:12.5px;color:var(--muted-2)}
/* Changing how you sign in is a deliberate act, so it stays folded away. */
.setdrop{border-top:1px solid var(--line)}
.setdrop>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:10px;
  padding:11px 2px;font-size:14px}
.setdrop>summary::before{content:"\25B8";color:var(--muted-2);font-size:11px;
  display:inline-block;transition:transform .12s}
.setdrop[open]>summary::before{transform:rotate(90deg)}
.setdrop>summary:hover{color:var(--text)}
.setdrop>summary .muted2{margin-left:auto}
/* An author `display:` beats the UA rule that hides a closed <details>,
   whatever the specificity — so anything in here with a display of its own
   needs saying twice. The links form is flex; the language grid is grid.
   Both showed in full while collapsed until this line existed. */
.setdrop:not([open])>form,.setdrop:not([open])>.langpick-grid{display:none}
/* No top rule here: this one sits under its own <h2>, not between rows. */
.langdrop{border-top:0}
.langdrop>summary{padding-top:2px}
.setedit{display:flex;flex-direction:column;gap:6px;max-width:360px;padding:0 2px 14px}
.setedit .btn{align-self:flex-start;margin-top:6px}

/* ---- the sky map ---- */
.skymap{position:relative;border:1px solid rgba(246,232,166,.15);border-radius:22px;overflow:hidden;
  background:#05070f;box-shadow:inset 0 1px 0 rgba(246,232,166,.08), 0 26px 64px -30px rgba(0,0,0,.8)}
.skymap canvas{display:block;width:100%;cursor:grab;touch-action:none}
.skymap canvas.grabbing{cursor:grabbing}
/* Over something openable. Last, so it beats `grab` — and `grabbing` is listed
   after it again below, because a drag in progress outranks whatever the
   pointer happens to be over. */
.skymap canvas.on-body{cursor:pointer}
.skymap canvas.grabbing,.skymap canvas.on-body.grabbing{cursor:grabbing}
.sky-tools{position:absolute;top:12px;right:12px;z-index:3;display:flex;flex-direction:column;gap:6px}

/* Fullscreen. `:fullscreen` is what the browser sets on the element it promoted,
   so the map only has to say what it looks like once it is filling the screen:
   no rounded corners cutting into the edge, no border, and a canvas that takes
   the height it is finally allowed to have. */
/* Only the HOST is sized here. The canvas gets its size inline from
   skymap.js, which has to set the backing store to match — a stylesheet
   height for it would either lose to the inline style or win and leave the
   drawing stretched. */
.skymap:fullscreen{border-radius:0;border:0;width:100vw;height:100vh}
/* WebKit spells the pseudo-class differently and will not parse a rule list
   that mentions the standard one, so the two cannot be combined. */
.skymap:-webkit-full-screen{border-radius:0;border:0;width:100vw;height:100vh}
/* The fullscreen button ships with this class and skymap.js removes it once it
   knows the browser can honour a press. So it is absent with scripting off and
   absent where there is no Fullscreen API — the two cases where it would be a
   control that does nothing. */
.sky-tools .nofs{display:none}
.sky-hint{position:absolute;bottom:10px;left:0;right:0;text-align:center;margin:0;font-size:11.5px;
  color:rgba(215,208,234,.45);font-style:italic;pointer-events:none}
/* The seven wanderers, listed under the map: each says which sign it stands in
   and which sephirah it rules, so the map and the tree read as one system. */
.sky-list{list-style:none;margin:0;padding:12px;display:grid;gap:8px;
  grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
  border-top:1px solid rgba(246,232,166,.12)}
.sky-list li{display:flex;flex-direction:column;gap:2px}
.sky-list button{width:100%;text-align:left;border:1px solid rgba(246,232,166,.14);border-radius:10px;
  background:rgba(246,232,166,.03);color:var(--read);cursor:pointer;padding:8px 10px;font:inherit}
.sky-list button:hover,.sky-list button:focus-visible{border-color:rgba(246,232,166,.4);outline:none}
.sky-list b{display:block;font-size:13.5px;color:#fff6c6;font-weight:600}
.sky-in{display:block;font-size:11.5px;color:rgba(246,232,166,.7)}
.sky-seph{font-size:11px;color:var(--muted-2);padding-left:10px}
.sky-seph:hover{color:var(--gold)}
.sky-credit{margin:14px 0 26px;text-align:center;font-size:11px;line-height:1.6;color:var(--muted-2)}
@media(max-width:520px){.sky-list{grid-template-columns:repeat(auto-fill,minmax(128px,1fr))}}

/* The hover label. `pointer-events:none` matters: a label that sits under the
   cursor would take the next hover from the star it is describing, and the
   thing would flicker as you moved one pixel. */
.sky-tip{position:absolute;z-index:4;pointer-events:none;max-width:190px;
  padding:6px 10px;border-radius:9px;
  border:1px solid rgba(246,232,166,.30);background:rgba(5,7,15,.92);
  color:#fff6c6;font-size:12.5px;font-weight:600;line-height:1.35;
  box-shadow:0 8px 22px -10px rgba(0,0,0,.9);
  opacity:1;transition:opacity .18s ease}
.sky-tip span{display:block;font-weight:400;font-size:11px;
  color:rgba(246,232,166,.62)}
/* Faded rather than hidden, so the half-second grace period ends in a fade and
   not a blink. `hidden` would cut the transition off at the first frame. */
.sky-tip.out{opacity:0}
.sky-tip[hidden]{display:none}

/* What a star is. Anchored to the map so it comes along into fullscreen. */
.sky-info{position:absolute;z-index:5;top:12px;right:12px;width:min(272px,46%);
  padding:14px 16px 13px;border-radius:14px;
  border:1px solid rgba(246,232,166,.28);background:rgba(5,7,15,.94);
  box-shadow:0 18px 44px -20px rgba(0,0,0,.95);
  backdrop-filter:blur(6px)}
.sky-info[hidden]{display:none}
.sky-info-name{display:block;font-size:16px;color:#fff6c6;font-weight:700;
  padding-right:22px;line-height:1.25}
.sky-info-body{display:grid;gap:5px;margin-top:10px}
.sky-info-body div{display:flex;align-items:baseline;justify-content:space-between;
  gap:12px;font-size:12.5px}
.sky-info-body span{color:rgba(215,208,234,.6)}
.sky-info-body b{color:#e9e3ff;font-weight:600;font-variant-numeric:tabular-nums}
.sky-info-note{margin:10px 0 0;font-size:11.5px;line-height:1.5;
  color:rgba(246,232,166,.62)}
.sky-info-note[hidden]{display:none}
.sky-info-x{position:absolute;top:8px;right:9px;width:24px;height:24px;
  display:flex;align-items:center;justify-content:center;
  border:0;border-radius:7px;background:transparent;cursor:pointer;
  color:rgba(215,208,234,.65);font-size:14px;line-height:1}
.sky-info-x:hover{color:#fff6c6;background:rgba(246,232,166,.10)}

/* Fullscreen: the wanderers move INSIDE the map, along the bottom.
   Below the canvas they would be off the screen entirely — the canvas is the
   whole viewport — and they are the one piece of the page somebody still wants
   while looking closely, since each one is also a way to fly to that body. */
.skymap:fullscreen .sky-list{
  position:absolute;left:0;right:0;bottom:0;z-index:4;
  border-top:1px solid rgba(246,232,166,.14);
  background:linear-gradient(180deg,rgba(5,7,15,0),rgba(5,7,15,.92) 34%);
  padding:16px 18px 14px;
  grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
  max-height:38vh;overflow:auto}
/* Separate rules, never one list: a selector list containing a pseudo-class
   the browser does not know is dropped ENTIRELY, so naming both spellings
   together would apply in neither. */
.skymap:-webkit-full-screen .sky-list{
  position:absolute;left:0;right:0;bottom:0;z-index:4;
  border-top:1px solid rgba(246,232,166,.14);
  background:linear-gradient(180deg,rgba(5,7,15,0),rgba(5,7,15,.92) 34%);
  padding:16px 18px 14px;
  grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
  max-height:38vh;overflow:auto}
/* The hint would sit under the list it just gained. */
.skymap:fullscreen .sky-hint{display:none}
.skymap:-webkit-full-screen .sky-hint{display:none}

/* ---- payouts and the stub checkout ---- */
.wallet-payout{margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
.wallet-payout h3{font-size:14px;font-weight:700;margin-bottom:10px}
.wallet-payout .input[name=destination]{margin-top:8px}
.wallet-payout .pay-row{margin-top:8px}
.wpay{flex-wrap:wrap}
.wpay .wtag.ok{color:var(--ok);border-color:color-mix(in srgb, var(--ok) 34%, transparent)}
.wpay .wtag.no{color:var(--danger);border-color:color-mix(in srgb, var(--danger) 34%, transparent)}
/* The reason a payout was refused. Full width under the row, because it is a
   sentence and the row above it is a line of chips. */
.wpay-note{flex-basis:100%;margin:6px 0 0;font-size:12.5px;line-height:1.55;
  color:var(--muted)}
/* The refuse control carries its reason with it, so the two travel together
   and a reason cannot be sent along with the money by mistake. */
.mrow-reject{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.mrow-reject .input{width:min(240px,42vw);padding:6px 10px;font-size:13px}

.checkout{max-width:520px;margin:0 auto}
.checkout h1{font-size:24px;margin:14px 0 6px}
.checkout-amount{display:flex;align-items:baseline;justify-content:space-between;
  gap:14px;margin:18px 0;padding:14px 16px;border-radius:var(--r);
  border:1px solid var(--line);background:var(--field)}
.checkout-amount span{color:var(--muted);font-size:13px}
.checkout-amount b{font-size:26px;color:var(--gold);font-variant-numeric:tabular-nums}
.checkout-acts{display:flex;gap:10px;flex-wrap:wrap}
/* Loud on purpose: this page hands out money, and somebody who reaches it in
   production needs to trip over that rather than read past it. */
.warnbar{margin:0 0 6px;padding:10px 14px;border-radius:var(--r);
  border:1px solid color-mix(in srgb, var(--danger) 45%, transparent);
  background:color-mix(in srgb, var(--danger) 12%, transparent);
  color:var(--danger);font-size:12.5px;font-weight:600;line-height:1.5}

/* ---- admin: the members list ---- */
/* A list you scan, not a control panel. Every action lives on the member's own
   page — the split between the two was what made this unreadable. */
.urow{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:11px 14px;margin-bottom:8px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel)}
.urow-who{flex:1 1 240px;min-width:0}
.urow-who b{display:block;font-size:14.5px}
.urow-who .em{font-size:12.5px;color:var(--muted)}
.urow-tags{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.urow-tags .uid{font-size:11.5px;color:var(--muted-2)}
.urow-act{margin-left:auto}
.pill.warn{background:rgba(234,179,8,.14);color:var(--warn,#eab308);border-color:rgba(234,179,8,.32)}
/* Member page: the moderation and wallet rows. */
.amod{display:flex;flex-direction:column;gap:10px}
.amod-row{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin:0}
.amod-row .input{max-width:190px}

/* ---- admin member page: rights as a list, wallet, cards ---- */
/* One right per row, each saying in words what it allows. The old grid showed
   bit numbers, which read like a hex dump and told nobody anything. */
.frows{list-style:none;margin:0 0 14px;padding:0;display:flex;flex-direction:column;gap:2px}
.frow{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:10px;
  border:1px solid transparent}
.frow:hover{border-color:var(--line);background:rgba(255,255,255,.02)}
.frow-main{display:flex;align-items:flex-start;gap:11px;flex:1;cursor:pointer;margin:0}
.frow-main input{accent-color:var(--violet-d);width:17px;height:17px;margin-top:2px;cursor:pointer}
.frow-txt b{display:block;font-size:14px;font-weight:600}
.frow-hint{display:block;font-size:12.5px;color:var(--muted-2);line-height:1.45;margin-top:1px}
.frow.locked{opacity:.5}
.frow.locked .frow-main,.frow.locked input{cursor:not-allowed}
.frow-lock{font-size:13px;opacity:.6}
/* Adding, taking away and overwriting are three decisions, so three buttons. */
.amoney{max-width:340px}
.amoney-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.amoney-set{margin-left:auto}
.acards{max-width:340px}
.acards select.input{width:100%}
.acards-n{width:110px}
/* The identity card holds one more form inside it. */
.asub{margin-top:18px;padding-top:14px;border-top:1px solid var(--line)}
.asub h3{font-size:14.5px;margin-bottom:10px}

/* A reader's services are panels: open by default, because they are why most
   people opened the page — and plainly closeable, because a wall underneath is
   a long way down past four of them.

   The marker is a real element in the template rather than a CSS escape: an
   escape has to survive being written by a script, and one that does not fails
   silently as a stray control character with no glyph, which is exactly how
   this row came to look like plain text. */
.offer > summary{cursor:pointer;list-style:none;margin-bottom:0;border-radius:10px;
  padding:4px 6px;margin-left:-6px;margin-right:-6px;transition:background .15s}
.offer > summary:hover{background:rgba(255,255,255,.05)}
.offer > summary:hover h3{color:var(--violet)}
.offer-chev{flex:0 0 auto;color:var(--muted-2);font-size:13px;line-height:1;
  transition:transform .15s;display:inline-block;align-self:center}
.offer:not([open]) .offer-chev{transform:rotate(-90deg)}
/* Which way the click goes, in words. The two are on the element as data
   attributes so the text stays translated without a line of script. */
.offer-toggle{flex:0 0 auto;font-size:11.5px;color:var(--muted-2);align-self:center}
.offer[open] .offer-toggle::after{content:attr(data-open)}
.offer:not([open]) .offer-toggle::after{content:attr(data-shut)}
.offer[open] > summary{margin-bottom:10px}
.offer-body{border-top:1px solid var(--line);padding-top:12px}
/* The headline and the professional bio, which sit under the name and are not
   the same thing as what somebody says about themselves. */
.prof-headline{font-size:14px;color:var(--read);margin:0 0 5px}
.prof-h{font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted-2);margin:0 0 7px;font-weight:600}
.prof-bio{margin-top:18px}
.prof-bio p{font-size:14px;color:var(--read);line-height:1.7;white-space:pre-wrap;margin:0}
.prof-specs{margin-top:18px}
.prof-facts .stars,.prof-facts .price{font-size:12.5px}
.prof .mhead-pay{margin-top:16px}

/* The two halves of a reader's profile, as tabs. Both panels are in the page;
   the script hides one. Without it the links are ordinary links and the server
   renders the right half. */
.ptabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin:4px 0 22px}
.ptab{padding:10px 16px;font-size:14px;color:var(--muted);text-decoration:none;
  border-bottom:2px solid transparent;margin-bottom:-1px;transition:.15s}
.ptab:hover{color:var(--text)}
.ptab.on{color:var(--text);border-bottom-color:var(--violet);font-weight:600}
.ppanel[hidden]{display:none}

/* The conversation under a post card: the last few, a way to more, and a box. */
.wcomments{margin-top:10px;padding-top:10px;border-top:1px solid var(--line)}
.wcomments .wreplies{margin-bottom:10px}
.wcomments-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.wmore{font-size:12.5px;color:var(--violet);text-decoration:none;flex:0 0 auto}
.wmore:hover{text-decoration:underline}
.wmore.busy{opacity:.5;pointer-events:none}
.wreply-quick{display:flex;gap:8px;flex:1;min-width:200px;margin:0;align-items:center}
.wreply-quick .input{flex:1;min-width:0;font-size:13px;padding:8px 11px}
/* A paperclip sized for a row rather than a composer. */
.wattach-sm{width:32px;height:32px;font-size:14px;border-radius:9px}
/* Files on a reply: small, because a reply is a response and not a post. */
.wreply-media{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.wreply-media .nwmedia{margin:0;max-width:200px}
.wreply-media img,.wreply-media video{max-width:200px;max-height:150px;border-radius:9px}
.wreply-media audio{width:220px}
.wreply-form-foot{display:flex;align-items:center;gap:10px;width:100%}

/* The two rarer post actions, as buttons in the comment box's footer. They sit
   beside the paperclip because they belong to the same row of "things you can
   do here", and they are links rather than buttons — each opens a form of its
   own, and HTML has no nested forms. */
.wact-btn{display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;border-radius:9px;font-size:14px;line-height:1;
  border:1px solid var(--line);color:var(--muted);transition:.15s;flex:none}
.wact-btn:hover{border-color:var(--violet);color:var(--text)}
/* Open. Says which one you pressed, and its link closes it again. */
.wact-btn.on{border-color:var(--violet-d);background:var(--violet-d);color:#fff}

/* The panel one of them opens, directly under the box it belongs to. */
.wreply-extra{margin-top:10px;padding:14px 16px;border-radius:var(--r-lg);
  border:1px solid var(--line);background:var(--panel)}
.wreply-extra > b{display:block;font-size:13.5px;margin-bottom:10px}
.wreply-extra .label{margin-top:10px}
.wreply-extra .wreply-form-foot{margin-top:12px}

/* Undo, on a tombstone. Inline with the "removed" line rather than under it:
   the two are one sentence — this is gone, unless you say otherwise. */
.wundo{display:inline}
.wundo-btn{border:0;background:none;padding:0 0 0 8px;cursor:pointer;font:inherit;
  font-size:inherit;color:var(--violet);text-decoration:underline;
  text-underline-offset:2px}
.wundo-btn:hover{color:var(--text)}

/* Paging the whole conversation, on a post's own page. */
.wpager{display:flex;flex-wrap:wrap;gap:6px;margin:18px 0 0}
.wpage{min-width:32px;text-align:center;padding:6px 9px;border-radius:9px;font-size:13px;
  color:var(--muted);text-decoration:none;border:1px solid var(--line)}
.wpage:hover{color:var(--text);border-color:var(--line-2)}
.wpage.on{color:var(--text);border-color:var(--violet);background:rgba(124,58,237,.14)}

/* Editing your listing, from the tab that shows it. */
.uslugi-edit{margin:0 0 16px}

/* The formatting buttons above a prose box. Shortcuts for typing the markup
   the engine already reads — which is why the paragraph explaining it went. */
.fmtbar{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin-bottom:6px}
.fmtbar button{min-width:30px;height:28px;padding:0 7px;border:1px solid transparent;
  border-radius:8px;background:none;cursor:pointer;font:inherit;font-size:12.5px;
  color:var(--muted);transition:.12s;line-height:1}
.fmtbar button:hover{color:var(--text);background:rgba(255,255,255,.07);border-color:var(--line)}
.fmtbar-sep{width:1px;height:16px;background:var(--line);margin:0 4px}

/* The bubble of faces under a reaction chip. Positioned against the chip's own
   form, which is the only thing that wraps exactly one chip. */
.wreact-f{position:relative}
.rbub{position:absolute;left:0;bottom:calc(100% + 8px);z-index:70;
  border-radius:12px;border:1px solid var(--line);background:var(--panel-solid);
  box-shadow:0 12px 34px rgba(0,0,0,.5);padding:6px;min-width:150px;
  animation:rbub-in .12s ease-out}
@keyframes rbub-in{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}
/* A gap between the chip and the bubble would drop the hover on the way across,
   so the bubble reaches down to meet it with a transparent lip. */
.rbub::after{content:"";position:absolute;left:0;right:0;top:100%;height:10px}
.rbub-in{display:flex;flex-direction:column;gap:1px}
.rbub-one{display:flex;align-items:center;gap:8px;padding:4px 8px 4px 4px;border-radius:9px;
  text-decoration:none;color:var(--text);font-size:12.5px;white-space:nowrap}
.rbub-one:hover{background:rgba(255,255,255,.07);color:var(--violet)}
.rbub-name{overflow:hidden;text-overflow:ellipsis;max-width:180px}
.rbub-more{font-size:11.5px;color:var(--muted-2);padding:3px 8px 1px}

/* The whole list, in a small window rather than another tab. */
.rdlg-who{width:min(420px,92vw);min-height:0;max-height:80vh;resize:none;overflow:auto}
.rall{display:flex;flex-direction:column;gap:10px;padding:14px 16px 16px}
.rall-head{display:flex;align-items:center;gap:10px;font-size:14.5px}
.rall-list{display:flex;flex-direction:column;gap:2px}
.rall-one{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:10px;
  text-decoration:none;color:var(--text);font-size:13px}
.rall-one:hover{background:rgba(255,255,255,.06)}
.rall-e{font-size:15px;width:20px;text-align:center;line-height:1}
.rall-who{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.rall-who b{font-weight:600}

/* Reactions. A chip per symbol somebody used, and a picker to add one — both
   ordinary form posts, so the whole thing works with no script at all. */
.wreacts{display:flex;flex-wrap:wrap;align-items:center;gap:5px;margin-top:9px}
.wreacts form{margin:0}
.wreact{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:999px;
  border:1px solid var(--line);background:var(--panel-2);cursor:pointer;
  font:inherit;font-size:12.5px;color:var(--muted);transition:.15s;line-height:1.5}
.wreact-e{font-size:14px;line-height:1}
.wreact:hover{border-color:var(--line-2);color:var(--text)}
/* Yours reads as yours, without shouting. */
.wreact.on{border-color:var(--violet);background:rgba(124,58,237,.18);color:var(--text)}
.wreact-off{cursor:default}
.wreact-off:hover{border-color:var(--line);color:var(--muted)}
.wreact-add{padding:3px 10px}
.wreact-who{font-size:12px;color:var(--muted-2);text-decoration:none;margin-left:4px}
.wreact-who:hover{color:var(--violet)}

/* The controls for LEAVING a reaction — the quick heart and the picker — are
   not permanent furniture. Chips somebody actually left always show, because
   they are information; the empty controls appear when you go to use them.

   Three things this has to get right, and each one is a way it breaks:

   - Touch has no hover. The whole block is behind `hover: hover` and
     `pointer: fine`, so on a phone the controls simply stay visible; a control
     you can only reveal by hovering is a control a phone cannot reach.
   - A keyboard has no pointer either, so `:focus-within` on the row reveals
     them. `opacity`, not `visibility` or `display` — a hidden element is not
     focusable, and tabbing to something you cannot see is worse than either.
   `opacity` also keeps the space reserved, so nothing shifts under the cursor
   at the moment somebody goes to click. */
@media (hover: hover) and (pointer: fine) {
  .wreact-quiet,
  .mreact-quiet {
    opacity: 0;
    transition: opacity .12s;
  }
  .wpost:hover .wreact-quiet,
  .wreacts:focus-within .wreact-quiet,
  .msg:hover .mreact-quiet,
  .msg:focus-within .mreact-quiet {
    opacity: 1;
  }
}
/* The heart is the one symbol worth a button, so it gets the colour of one. */
.wreact-heart:hover,
.mreact-heart:hover {
  border-color: var(--danger);
  color: var(--danger);
}


/* Who reacted, and who passed it on. */
.wwho-list{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:6px}
.wwho-one{display:inline-flex;align-items:center;gap:7px;padding:5px 11px 5px 6px;
  border:1px solid var(--line);border-radius:999px;background:var(--panel-2);
  text-decoration:none;color:var(--text);font-size:12.5px;transition:.15s}
.wwho-one:hover{border-color:var(--violet)}
.wwho-e{font-size:14px;line-height:1}

/* The strip across the top of a profile. Cropped by the box rather than by us:
   we do not know how tall it will be on the reader's screen. */
.prof-banner{margin:0 0 -46px;border-radius:16px;overflow:hidden;
  height:clamp(120px,22vw,240px);background:var(--panel-2);border:1px solid var(--line)}
.prof-banner img{width:100%;height:100%;object-fit:cover;display:block}
.prof-banner[hidden]{display:none}
/* The avatar rides up over the banner's lower edge, the way it does everywhere
   this pattern is used. */
.prof-under-banner{padding-top:0}
.prof-under-banner .prof-top{position:relative;z-index:1}
.prof-under-banner .av-lg{border:3px solid var(--bg);background-clip:padding-box}
@media(max-width:560px){
  .prof-banner{margin-bottom:-34px;border-radius:12px}
}

/* Answering a post: a native <dialog>, so the browser owns the modal, the
   focus trap and Escape, and there is no overlay of ours to get wrong. */
/* `--panel` is 4.5% white: it is a tint meant to sit on the page. A <dialog>
   is in the top layer with nothing behind it but its own backdrop, so that tint
   made the window all but see-through. `--panel-solid` is the token for exactly
   this, and it is defined in all three theme states. */
.rdlg{width:min(600px,92vw);padding:0;border:1px solid var(--line-2);border-radius:16px;
  background:var(--panel-solid);color:var(--text);
  box-shadow:0 24px 70px rgba(0,0,0,.6);
  /* Never taller than the screen, and draggable by the corner past that. A post
     with a picture in it made the window overrun the viewport and the bottom
     was simply gone — nothing scrolled, because a <dialog> sizes to content and
     the content had no ceiling. */
  max-height:88vh;min-height:320px;min-width:320px;max-width:96vw;
  resize:both;overflow:hidden;
  /* Dragged by its header, so it is placed rather than centred once it moves. */
  margin:auto}
.rdlg[data-moved]{margin:0}
.rdlg::backdrop{background:rgba(6,4,14,.78)}
.rdlg-form{display:flex;flex-direction:column;gap:12px;padding:16px 18px 18px;margin:0;
  /* The form is the height of the window, and the quote takes the slack. The
     header, the box and the buttons keep their own height so they are always
     reachable however tall the thing being answered is. */
  height:100%;min-height:0;box-sizing:border-box}
.rdlg-head,.rdlg-foot,.rdlg-what{flex:0 0 auto}
.rdlg-form>.textarea{flex:0 0 auto}
.rdlg-head{display:flex;align-items:center;gap:10px;font-size:14.5px;
  cursor:grab;user-select:none;margin:-4px -4px 0;padding:4px}
.rdlg-head:active{cursor:grabbing}
.rdlg.dragging,.rdlg.dragging .rdlg-head{cursor:grabbing}
/* The buttons in the bar are not a handle. */
.rdlg-head button{cursor:pointer}
.rdlg-x{background:none;border:0;color:var(--muted-2);font-size:15px;cursor:pointer;
  padding:4px 6px;border-radius:8px;line-height:1}
.rdlg-x:hover{color:var(--danger);background:rgba(255,255,255,.05)}
.rdlg .textarea{width:100%;resize:vertical;min-height:86px}
.rdlg-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.rdlg-what{font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted-2);margin:4px 0 0}
/* What you are answering, read-only. Dimmed and unclickable, because it is
   theirs: nothing in this window can change a word of it. */
.rdlg-quote{flex:1 1 auto;min-height:0;overflow-y:auto;border:1px solid var(--line);
  border-left:2px solid var(--violet);border-radius:12px;background:var(--panel-2);
  padding:4px 12px;pointer-events:none;opacity:.9}
/* A picture in the quoted post is a thumbnail here, not the reason the window
   ran off the screen. */
.rdlg-quote img,.rdlg-quote video{max-height:180px;width:auto}
.rdlg-quote .wpost{border:0;background:none;padding:8px 0}

/* The admin's eye: the same corner as the pencil, and never both, because they
   are the two halves of "is this me". Yellow, so it never reads as a control a
   member has. A link now — it used to open a dropdown with three lines of the
   account card, which is a worse version of the account card. */
.prof-eye{color:var(--gold);border-color:rgba(212,175,55,.4)}
.prof-eye:hover{color:var(--gold);border-color:var(--gold);background:rgba(212,175,55,.12)}


/* The nickname in the corner, and what it opens. A <details>, so the browser
   does the opening; the script only closes it when you look away. */
.umenu{position:relative}
.umenu-btn{display:inline-flex;align-items:center;gap:6px;cursor:pointer;list-style:none;
  padding:7px 13px;border-radius:10px;font-size:13px;color:var(--text);
  border:1px solid var(--line);background:var(--panel);transition:.15s}
.umenu-btn:hover{border-color:var(--violet)}
.umenu-name{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The picture sits between the name and the chevron. Small enough that the
   header does not grow around it: 26px against a 13px line of text. */
.umenu-av{width:26px;height:26px;font-size:0;margin-left:1px}
.umenu-av .av-init{font-size:11px}
.umenu-chev{font-size:10px;color:var(--muted-2);transition:transform .15s}
.umenu[open] .umenu-chev{transform:rotate(180deg)}
.umenu-pop{position:absolute;right:0;top:calc(100% + 6px);z-index:60;min-width:190px;
  display:flex;flex-direction:column;padding:6px;border-radius:12px;
  border:1px solid var(--line);background:var(--panel-solid);
  box-shadow:0 14px 40px rgba(0,0,0,.45)}
.umenu-pop form{margin:0;display:flex}
.umenu-pop a,.umenu-pop button{display:block;width:100%;text-align:left;padding:9px 11px;
  border:0;border-radius:8px;background:none;cursor:pointer;font:inherit;font-size:13.5px;
  color:var(--text);text-decoration:none;transition:background .12s}
.umenu-pop a:hover,.umenu-pop button:hover{background:rgba(255,255,255,.06);color:var(--violet)}
/* Signing out is the one that leaves, so it reads apart from the rest. */
.umenu-pop form{border-top:1px solid var(--line);margin-top:4px;padding-top:4px}
.umenu-pop form button{color:var(--muted)}
.umenu-pop form button:hover{color:var(--danger)}

/* Choosing a language: in settings, and at the very bottom of the front page
   for somebody who cannot read the rest of it. Named in its own words there,
   because a two-letter code is no help to the person who needs it. */
.langpick{display:inline-flex;gap:10px;align-items:center;font-size:13px}
.langpick .on{color:var(--text);font-weight:600}
.langpick a{color:var(--muted)}
.langpick a:hover{color:var(--violet)}
.foot-lang{display:flex;justify-content:center;gap:18px;margin:22px 0 0;font-size:13px}
.foot-lang .on{color:var(--text)}
.foot-lang a{color:var(--muted)}
.foot-lang a:hover{color:var(--violet)}

/* What you do with this person, above the tabs — because a tab hides
   everything inside it, and none of this belongs to one half of the page. */
.prof-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:4px 0 14px}
.prof-bar form{margin:0}
.prof-counts{font-size:13px;color:var(--muted-2)}
.prof-counts b{color:var(--text);font-weight:600}
.btn.subbed{color:var(--muted)}
.btn.subbed:hover{color:var(--danger);border-color:var(--danger)}

/* Editing your own page, from the corner of it. */
.prof{position:relative}
.prof-edit{display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:10px;color:var(--muted);
  /* A border by default, not only on hover: as a bare dim glyph in a corner it
     was routinely missed, and it is the only way to edit your own page. */
  border:1px solid var(--line);background:var(--panel-2);transition:.15s;flex:0 0 auto}
.prof-edit svg{width:17px;height:17px}
.prof-edit:hover{color:var(--violet);border-color:var(--line);background:var(--panel-2)}

/* The editor: a preview of the page, then everything that page shows. */
.pedit{display:flex;flex-direction:column}
.pedit .prof-h{margin-top:22px}
.pedit .prof-h:first-of-type{margin-top:0}
.pedit-bar{display:flex;align-items:center;gap:10px;padding:12px 0;
  border-bottom:1px solid var(--line);margin-bottom:18px}
.pedit-bar:last-child{border-bottom:0;border-top:1px solid var(--line);margin:22px 0 0}
.pedit-preview{border:1px solid var(--line);border-radius:16px;background:var(--panel);
  padding:18px 20px}
.pedit-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.pedit-clear{display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--muted)}
.pedit .lnkrows{margin-top:4px}

/* ---- profiles: who somebody is, above the wall they write on ---- */
/* The header states a few facts and gets out of the way; the wall below it is
   what the page is for, so nothing up here competes with a post for attention. */
.prof{margin:6px 0 26px;padding-bottom:20px;border-bottom:1px solid var(--line)}
.prof-top{display:flex;gap:20px;align-items:center}
.prof-who{min-width:0}
.prof-who h1{font-size:26px;margin:0 0 4px;line-height:1.15}
.prof-line{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 5px}
.prof-handle{font-size:13px;color:var(--muted-2)}
.prof-badge{display:inline-flex;align-items:center;gap:4px;font-size:11.5px;
  padding:2px 9px;border-radius:999px;text-decoration:none;
  color:var(--violet);border:1px solid var(--line-2);background:rgba(124,58,237,.12)}
.prof-badge:hover{background:rgba(124,58,237,.22)}
.prof-facts{font-size:12.5px;color:var(--muted-2);margin:0}
.prof-about{margin:16px 0 0;font-size:14.5px}
.prof-about.nwbody{margin-top:16px}
/* The link chips sit under the words about themselves, not beside the name. */
.prof .lnkchips{margin:16px 0 0}
.prof-acts{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:18px}
.prof-acts form{margin:0}
@media(max-width:560px){
  .prof-top{gap:14px}
  .prof-who h1{font-size:21px}
}

/* ---- walls: a member's page of posts, and the shared feed ---- */
/* One card style, used on a wall, in the feed and on a post's own page, because
   they are the same thing seen from three places. */
.wowner{display:flex;gap:18px;align-items:center;margin:6px 0 22px}
.wowner-who{min-width:0}
.wowner-who h1{font-size:24px;margin:0 0 3px}
.wowner-acts{display:flex;flex-wrap:wrap;gap:14px;align-items:center;margin:6px 0 0;font-size:13px}
.wowner-msg{margin:0}
.wmine{margin:0 0 16px;font-size:13px}

.wfeed{display:flex;flex-direction:column;gap:14px}
.wpost{border:1px solid var(--line);border-radius:16px;background:var(--panel);padding:14px 16px 8px}
.wpost.gone{background:transparent;border-style:dashed}
/* A tombstone says one quiet thing and takes up no more room than it needs. */
.wgone{color:var(--muted-2);font-style:italic;font-size:13px;margin:2px 0 8px}

.whead{display:flex;align-items:center;gap:11px;margin-bottom:10px}
.wwho{min-width:0;display:flex;flex-direction:column;line-height:1.3}
.wname{font-weight:600;font-size:14.5px;color:var(--text);text-decoration:none}
.wname:hover{color:var(--violet)}
.wmeta{font-size:11.5px;color:var(--muted-2)}
.wmeta a{color:inherit;text-decoration:none}
.wmeta a:hover{color:var(--violet)}
/* The body is prose-engine output, so it borrows the article styling wholesale
   rather than growing a second set of rules that can drift from it. */
.wpost .nwbody{margin-top:0}
.wbody{font-size:14.5px}

/* What a repost passes on, inset one level. It can never nest again: reposting
   a repost records the original. */
.wsource{border:1px solid var(--line);border-left:2px solid var(--violet);
  border-radius:12px;background:var(--panel-2);padding:10px 13px 4px;margin:10px 0 4px}
.wsource-head{display:flex;align-items:center;gap:8px;text-decoration:none;color:inherit;
  margin-bottom:7px;font-size:13px}
.wsource-head:hover b{color:var(--violet)}
.wsource-gone{padding:9px 13px}
.wsource .nwbody,.wsource .wbody{margin-top:0;font-size:13.5px}

.wacts{display:flex;align-items:center;gap:4px;margin-top:8px;padding-top:6px;
  border-top:1px solid var(--line)}
.wacts form{margin:0}
.wgrow{flex:1}
.wact{display:inline-flex;align-items:center;gap:5px;background:none;border:0;
  padding:6px 9px;border-radius:9px;cursor:pointer;font:inherit;font-size:13px;
  color:var(--muted-2);text-decoration:none;transition:.15s}
.wact span{font-size:12px}
.wact:hover{color:var(--text);background:rgba(255,255,255,.05)}
/* Not a button when there is nobody to act: a signed-out visitor still sees the
   counts, and nothing that pretends to be pressable. */
.wact-off{cursor:default}
.wact-off:hover{color:var(--muted-2);background:none}
.wmark.on{color:var(--violet)}
.wact-del:hover{color:var(--danger)}

/* Writing something. */
.wcompose{border:1px solid var(--line);border-radius:16px;background:var(--panel);
  padding:12px 14px 8px;margin-bottom:18px}
.wcompose .textarea{width:100%;min-height:64px;resize:vertical}
.wcompose-foot{display:flex;align-items:center;gap:10px;margin-top:9px}
.wattach{flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  width:38px;height:38px;border:1px solid var(--line);border-radius:11px;cursor:pointer;
  font-size:17px;background:var(--field);transition:.15s}
.wattach:hover{border-color:var(--violet)}
/* The emoji button in a dialog reuses `.wattach`; as a <button> it needs the
   UA font and padding reset that a <label> did not. */
button.wattach{padding:0;font-family:inherit;line-height:1;color:var(--text)}
button.wattach[hidden]{display:none}
/* The share window's word field, with the emoji button beside it. */
.sdlg-word{display:flex;gap:8px;align-items:center}
.sdlg-word .input{flex:1;min-width:0}
.wattach-names{font-size:12px;color:var(--violet);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;max-width:40%}

/* The two heavier actions, on a post's own page. */
.wtools{display:flex;flex-direction:column;gap:6px;margin:14px 0 22px}

.wreplies-head{font-size:12px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted-2);margin:24px 0 12px;font-weight:600}
.wreply-form{display:flex;flex-direction:column;gap:8px;align-items:flex-start;margin-bottom:18px}
.wreply-form .wattach-names{max-width:50%}
.wreply-form .textarea{width:100%;resize:vertical}
.wreplies{display:flex;flex-direction:column;gap:12px}
.wreply{display:flex;gap:10px}
.wreply.gone{opacity:.7}
.wreply-main{flex:1;min-width:0}
.wreply-top{display:flex;align-items:center;gap:8px}
.wreply-top form{margin:0}
.wreply-body{font-size:14px;color:var(--read);line-height:1.55;white-space:pre-wrap;
  word-break:break-word;margin-top:2px}

/* A post passed into a private message: a card, never the post itself. */
.msg-post{display:flex;flex-direction:column;gap:5px;text-decoration:none;color:inherit;
  border:1px solid var(--line);border-left:2px solid var(--violet);border-radius:10px;
  background:rgba(0,0,0,.18);padding:8px 10px;margin-bottom:6px;max-width:100%}
.msg-post:hover{border-color:var(--violet)}
.msg-post-who{display:flex;align-items:center;gap:7px;font-size:12.5px}
.msg-post-teaser{font-size:12.5px;color:var(--muted-2);line-height:1.4}
.msg-post-bare{display:inline-block;font-size:12.5px;color:var(--violet)}

@media(max-width:560px){
  .wowner{gap:12px}
  .wowner-who h1{font-size:20px}
  .wpost{padding:12px 13px 6px;border-radius:14px}
  .wact{padding:6px 7px}
}

/* ---- profile links: social networks and messengers ---- */
/* The mark is a lettermark, not a logo: we do not have the artwork for ten
   brands, and a hand-drawn approximation of one reads as a forgery. It carries
   the brand colour instead, which is what makes the row scannable. */
/* The colour arrives as an `R G B` triplet in --lnk, so a translucent fill is
   one rgb() away. Each is preceded by a flat fallback for a browser too old for
   the space-separated form. */
.lnkmark{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:9px;
  color:#7C3AED;color:rgb(var(--lnk,124 58 237));
  background:rgba(124,58,237,.14);background:rgb(var(--lnk,124 58 237) / .16);
  border:1px solid rgba(124,58,237,.3);border:1px solid rgb(var(--lnk,124 58 237) / .38)}
/* The mark inherits the chip's colour through `fill:currentColor`, so one
   custom property tints the whole thing and there is no per-brand rule. */
.lnkic{width:16px;height:16px;display:block}
/* Shown on a profile. */
.lnkchips{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 22px}
.lnkchip{display:inline-flex;align-items:center;gap:9px;padding:6px 12px 6px 7px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel-2);
  text-decoration:none;color:inherit;transition:border-color .15s,background .15s;max-width:100%}
a.lnkchip:hover{border-color:rgba(124,58,237,.5);
  border-color:rgb(var(--lnk,124 58 237) / .55);
  background:rgba(124,58,237,.08);background:rgb(var(--lnk,124 58 237) / .1)}
.lnkchip-plain{cursor:text;user-select:text}
.lnkwho{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.lnkwho b{font-size:12px;font-weight:600}
.lnkval{font-size:11.5px;color:var(--muted-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The editor: ten rows of mark + name + one box. */
.lnkedit{margin-top:4px}
.lnkrows{display:flex;flex-direction:column;gap:8px;margin-bottom:6px}
.lnkrow{display:flex;align-items:center;gap:10px}
.lnkrow .lnkname{flex:0 0 84px;font-size:12.5px;color:var(--muted)}
.lnkrow .input{flex:1;min-width:0}
@media(max-width:560px){
  /* The name sits above its box rather than squeezing it. */
  .lnkrow{flex-wrap:wrap}
  .lnkrow .lnkname{flex:1 1 auto}
  .lnkrow .input{flex:1 1 100%}
}

/* ---- chat: conversations beside the open thread ---- */
/* Both panes fill what is left under the app bar, and each scrolls on its own,
   so the composer never slides off the bottom of a long conversation. */
.chatpane{display:grid;grid-template-columns:300px minmax(0,1fr);gap:0;
  height:calc(100vh - 64px);max-width:1180px;margin:0 auto;
  border:1px solid var(--line);border-top:0;background:var(--panel)}
.chatpane-list{overflow-y:auto;border-right:1px solid var(--line);padding:8px}
.chatpane-all{display:block;padding:8px 10px;margin-bottom:6px;font-size:12.5px;
  letter-spacing:.04em;color:var(--muted-2)}
.chatpane-all:hover{color:var(--text)}
.chatpane-list .convo{border-radius:10px;margin-bottom:2px}
.chatpane-list .convo.on{background:rgba(124,58,237,.14)}
.chatpane-main{display:flex;flex-direction:column;min-width:0;min-height:0;padding:14px 16px 12px}
/* The thread takes the slack; the header and composer keep their own height.
   The 62vh cap belongs to the old single-pane page -- inside the pane it stops
   the thread growing into the space it is supposed to fill, which leaves the
   composer floating above a dead gap. */
.chatpane-main .chat-thread{flex:1;min-height:0;max-height:none;overflow-y:auto}
.chatpane-main .chat-headrow{flex:0 0 auto}
.chatpane-main .chat-composer{flex:0 0 auto}
/* The header is the way to the other person's page, when they have one. */
.chat-head-link{display:flex;align-items:center;gap:12px;text-decoration:none;color:inherit;
  border-radius:12px;transition:background .15s}
.chat-head-link:hover{background:rgba(255,255,255,.04)}
.chat-head-link:hover .chat-who b{color:var(--violet)}
.chat-head-link .chat-back{opacity:.55}
@media(max-width:820px){
  /* One pane on a phone: the list is its own page there. */
  .chatpane{grid-template-columns:1fr;height:calc(100vh - 56px)}
  .chatpane-list{display:none}
}

/* ---- money in a conversation ---- */
/* The card a money message wears instead of a plain bubble: an amount, and for
   a request a Pay button. Gold, because it is money; the request variant is
   quieter until it is paid. */
.msg-money{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:8px 12px;border-radius:12px;border:1px solid rgba(245,197,66,.5);
  background:rgba(245,197,66,.10);font-size:14px;font-weight:600;color:var(--read)}
.msg-money-ico{font-size:18px;line-height:1}
.msg-money-sum{color:var(--gold)}
.msg-money.req{border-color:var(--line-2);background:var(--panel-2)}
.msg-money.req .msg-money-sum{color:var(--read)}
.msg-money.paid{opacity:.75}
.msg-money-tag{font-size:11px;font-weight:700;color:var(--ok);text-transform:uppercase;letter-spacing:.04em}
.msg-money-pay{margin-left:auto}
.msg-money-pay .btn{margin:0}

/* The two money controls above the composer. `<details>` popovers: the summary
   is a round button like the paperclip, and the form floats over the thread. */
.chat-money-bar{display:flex;gap:8px;margin:10px 0 0}
.chat-money-pop{position:relative}
.chat-money-pop > summary{list-style:none;cursor:pointer}
.chat-money-pop > summary::-webkit-details-marker{display:none}
.chat-money-form{position:absolute;bottom:calc(100% + 8px);left:0;z-index:60;
  width:min(300px,86vw);display:flex;flex-direction:column;gap:6px;
  padding:14px;border-radius:14px;border:1px solid var(--line-2);
  background:var(--panel-solid);box-shadow:0 18px 50px rgba(0,0,0,.45)}
.chat-money-form > b{font-size:14px}
.chat-money-bal{font-size:12.5px;color:var(--muted-2);margin:0}
.chat-money-fee{font-size:12px;color:var(--gold);margin:2px 0 0}
.chat-money-form .btn{margin-top:6px}

/* Booking a service: a disclosure that opens the appointment form inline. */
.appoint{margin-top:14px}
.appoint > summary{list-style:none;cursor:pointer;display:inline-flex}
.appoint > summary::-webkit-details-marker{display:none}
.appoint-form{margin-top:12px;padding:16px;border-radius:var(--r-lg);
  border:1px solid var(--line);background:var(--panel);max-width:520px}
.appoint-form .lede{margin:0 0 10px;font-size:13px}
.appoint-foot{margin-top:14px}

/* The commission settings row in the admin money tab. */
.mcommission{margin:0 0 22px;padding:16px;border-radius:var(--r-lg);
  border:1px solid var(--line);background:var(--panel)}
.mcommission-row{display:flex;gap:16px;flex-wrap:wrap}

/* The money popover triggers wear the paperclip's round-button look but a class
   of their own, so nothing keys "the attach button" off them. */
.chat-money-btn{flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  width:38px;height:38px;border:1px solid var(--line);border-radius:11px;cursor:pointer;
  font-size:17px;background:var(--field);transition:.15s}
.chat-money-btn:hover{border-color:var(--violet)}

/* The two ask-first windows: the payment gate and the generic "are you sure".
   Both borrow `.rdlg` for their surface, border and backdrop, but not the
   draggable-window behaviour the reply/share dialogs need: no resize handle,
   no 320px floor, and — since neither has an `.rdlg-form` wrapper to carry it
   — their own padding. They size to their content and centre. */
.moneyc,.askc{width:auto;max-width:min(430px,94vw);min-width:0;min-height:0;
  resize:none;padding:20px 22px 22px;margin:auto}
.moneyc .rdlg-head,.askc .rdlg-head{cursor:default;margin:0 0 4px;padding:0}
.moneyc-q,.askc-q{font-size:15px;font-weight:600;margin:12px 0 6px}
.moneyc-left,.askc-note{font-size:13.5px;color:var(--muted);margin:0 0 14px}
.moneyc-left[hidden],.askc-note[hidden]{display:none}
.moneyc-warn{font-size:13px;font-weight:600;color:var(--danger);
  background:rgba(248,113,113,.10);border:1px solid rgba(248,113,113,.4);
  border-radius:10px;padding:10px 12px;margin:0}
/* The two buttons, centred, below the question. */
.moneyc .rdlg-foot,.askc .rdlg-foot{justify-content:center;margin-top:18px}
/* Nothing follows the note here, so it does not need the money window's gap. */
.askc-note:last-of-type{margin-bottom:0}

/* ---- a service in a conversation ---- */
/* The escrow card. Neutral while held, green once confirmed, red while disputed
   — the state you can read at a glance without the label. */
.msg-service{padding:10px 13px;border-radius:12px;border:1px solid var(--line-2);
  background:var(--panel-2);font-size:14px}
.msg-service .svc-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.svc-ico{font-size:17px;line-height:1}
.svc-status{font-size:11.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;color:var(--muted-2)}
.msg-service.svc-held{border-color:rgba(245,197,66,.45)}
.msg-service.svc-held .svc-status{color:var(--gold)}
.msg-service.svc-released{border-color:rgba(52,211,153,.45)}
.msg-service.svc-released .svc-status{color:var(--ok)}
.msg-service.svc-disputed{border-color:rgba(248,113,113,.5)}
.msg-service.svc-disputed .svc-status{color:var(--danger)}
.msg-service.svc-refunded{opacity:.8}
/* Offered: a proposal, in the brand violet, until the customer starts it. */
.msg-service.svc-offered{border-color:rgba(124,58,237,.45)}
.msg-service.svc-offered .svc-status{color:var(--violet)}
/* Running: money is held, work under way — the same gold the hold wore. */
.msg-service.svc-running{border-color:rgba(245,197,66,.45)}
.msg-service.svc-running .svc-status{color:var(--gold)}
/* Lapsed: an offer whose window closed, dimmed and settled. */
.msg-service.svc-lapsed{opacity:.7}
.msg-service.svc-lapsed .svc-status{color:var(--muted-2)}
.svc-acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px;align-items:center}
.svc-acts form{margin:0}
.svc-acts .btn{margin:0}
/* The accept window ticking down, next to the Start button or the wait note. */
.svc-left{font-size:12px;font-weight:700;color:var(--muted-2);
  font-variant-numeric:tabular-nums}
.svc-wait{margin:10px 0 0;font-size:12.5px;color:var(--muted-2);
  display:flex;gap:6px;align-items:center;flex-wrap:wrap}
/* One-click "offer this", hanging off the appointment that named the service. */
.msg-offer{margin-top:8px}
.msg-offer .btn{margin:0}
/* Stars on a settled card. Each star is its own submit button, so the control
   works with no script; hovering one dims the ones AFTER it, which is what makes
   a row of buttons read as a rating that fills. */
.svc-rate{display:flex;align-items:center;gap:2px;margin-top:10px;flex-wrap:wrap}
.svc-rate-label{font-size:12.5px;color:var(--muted-2);margin-right:6px}
.svc-rate .star{background:none;border:0;cursor:pointer;padding:0 2px;
  font-size:19px;line-height:1;color:var(--gold);transition:opacity .12s}
.svc-rate .star:hover ~ .star{opacity:.3}
.svc-rate .star:focus-visible{outline:2px solid var(--violet);border-radius:4px}

/* Moderation: the users search. IPs and browsers are links back into the same
   search, so "who else came from here" is a click. */
.mod-search{display:flex;gap:8px;margin-bottom:12px}
.mod-search .input{flex:1;min-width:0}
.mod-search .btn{margin:0}
.mod-tbl-wrap{overflow-x:auto}
.mod-users{width:100%;border-collapse:collapse;font-size:13px}
.mod-users th,.mod-users td{padding:7px 9px;border-bottom:1px solid var(--line);
  text-align:left;vertical-align:top}
.mod-users th{font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted-2)}
.mod-ua{max-width:280px;word-break:break-all}

/* Choosing a payment platform. Radio cards: the whole card is the label, so the
   hit area is the card and not a 13px circle. */
.wpay-choose{font-size:12.5px;color:var(--muted-2);margin:0 0 8px}
/* Platform cards: square-ish blocks, three across, each with its own Pay
   button. Grid rows stretch, so every card is the height of the tallest and
   the buttons line up however long a platform's description runs. */
.paylist{display:grid;gap:12px;margin-bottom:12px;grid-template-columns:repeat(3,minmax(0,1fr))}
@media(max-width:760px){.paylist{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:460px){.paylist{grid-template-columns:1fr}}
.paycard{display:flex;flex-direction:column;gap:4px;min-height:150px;
  padding:14px;border:1px solid var(--line-2);border-radius:12px;
  background:var(--panel-2);transition:.15s}
.paycard:hover{border-color:var(--violet);box-shadow:var(--glow)}
.paycard-name{font-size:14.5px}
.paycard-desc{font-size:12.5px;color:var(--muted-2)}
.paycard-meta{font-size:11.5px;color:var(--muted-2);line-height:1.35}
/* The sum, and the button pinned under it at the foot of the card. */
.paycard-pay{font-size:14px;font-weight:700;color:var(--gold);margin-top:8px;
  min-height:1.2em;font-variant-numeric:tabular-nums}
.paycard-go{margin-top:auto;width:100%}
