/* ============================================================================
   joestone.co portal, shared design system.

   Extracted from the per-page <style> blocks that had drifted across eight
   files. There is no build step: pages just <link> this. Keep it token-driven
   so a brand change is one edit here rather than eight.
   ========================================================================= */

:root{
  --bg:#0A0710; --bg-auth:#08060D; --bg2:#120C1B; --panel:#14101E; --panel-2:#1B1527;
  --violet:#A855F7; --violet-2:#C084FC; --violet-3:#7C3AED; --lilac:#CBB6F5;
  --ink:#F4F1F7; --body:#B4A9C4; --faint:#6E6480;
  --line:rgba(180,150,240,0.14); --line-2:rgba(180,150,240,0.08);
  --ok:#5EE39A; --warn:#F5C451; --hot:#F061C6; --bad:#F4638A;
  --on-violet:#12081D;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --disp:'Space Grotesk',system-ui,sans-serif;
  --sans:'Inter',system-ui,-apple-system,sans-serif;
  --ease:cubic-bezier(.16,1,.3,1);
  --maxw:1240px;
}

*{margin:0;padding:0;box-sizing:border-box}
/* The hidden attribute is how every page here shows and hides a section, and any
   class that sets a display value silently outranks it. That has already caused
   two bugs (an empty badge in every nav item, a stat cell that would not hide),
   so the attribute wins, always. */
[hidden]{display:none !important}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--bg); color:var(--body);
  font-family:var(--sans); font-weight:300; line-height:1.6;
  min-height:100vh; -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  /*
   * dvh, not vh. On iOS 100vh is the height with the toolbars retracted, which
   * is taller than what you can actually see, so a body floored at 100vh always
   * overflows by the height of the toolbar and every page can be dragged down
   * into empty space below the content. dvh tracks the visible viewport.
   * The vh line above stays as the fallback for anything without dvh.
   */
  min-height:100dvh;
  /* and stop the drag from chaining to the page behind once you do hit the end */
  overscroll-behavior-y:none;
}
body.auth{background:var(--bg-auth)}
a{color:inherit;text-decoration:none}
button,input,textarea,select{font:inherit;color:inherit}
::selection{background:rgba(168,85,247,.32);color:#fff}
:focus-visible{outline:2px solid var(--violet);outline-offset:3px;border-radius:3px}

.disp{font-family:var(--disp);color:var(--ink);letter-spacing:-.025em;line-height:1.12}
.mono{font-family:var(--mono)}
.kick{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.kick-v{color:var(--violet-2)}
.muted{color:var(--faint)}
.ink{color:var(--ink)}
.nowrap{white-space:nowrap}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ---------------------------------------------------------- background ---- */
.aurora{position:fixed;inset:-30%;z-index:0;filter:blur(94px) saturate(1.18);opacity:.55;pointer-events:none}
.blob{position:absolute;border-radius:50%;mix-blend-mode:screen;will-change:transform}
.b1{width:52vw;height:52vw;left:0;top:4%;background:radial-gradient(circle,#7C3AED,transparent 62%);animation:ha 26s ease-in-out infinite}
.b2{width:44vw;height:44vw;right:2%;top:0;background:radial-gradient(circle,#C026D3,transparent 62%);animation:hb 31s ease-in-out infinite}
.b3{width:48vw;height:48vw;left:30%;bottom:-10%;background:radial-gradient(circle,#6D28D9,transparent 62%);animation:hc 35s ease-in-out infinite}
@keyframes ha{0%,100%{transform:translate(0,0) scale(1)}50%{transform:translate(6%,8%) scale(1.12)}}
@keyframes hb{0%,100%{transform:translate(0,0) scale(1.05)}50%{transform:translate(-7%,5%) scale(.92)}}
@keyframes hc{0%,100%{transform:translate(0,0) scale(1)}50%{transform:translate(5%,-7%) scale(1.1)}}
.stars{position:fixed;inset:0;z-index:1;pointer-events:none}
.grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.045;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
.darken{position:fixed;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(120% 90% at 50% 40%, transparent 45%, rgba(10,7,16,.62) 100%)}
.page{position:relative;z-index:2}

/* ------------------------------------------------------------- brand ------ */
.brand{display:inline-flex;align-items:center;gap:12px;font-family:var(--disp);font-weight:600;
  font-size:15px;letter-spacing:.03em;color:var(--ink)}
.brand svg{width:26px;height:26px;flex:none;color:var(--violet)}
.brand.sm{font-size:13.5px;gap:10px}.brand.sm svg{width:22px;height:22px}

/* ------------------------------------------------------------ topbar ------ */
.topbar{position:sticky;top:0;z-index:20;border-bottom:1px solid var(--line-2);
  background:rgba(10,7,16,.72);backdrop-filter:blur(16px) saturate(1.2)}
.topbar .in{max-width:var(--maxw);margin:0 auto;padding:16px 32px;display:flex;align-items:center;gap:24px}
.topbar nav{margin-left:auto;display:flex;align-items:center;gap:24px;font-family:var(--mono);font-size:12px}
.topbar nav a{color:var(--faint);transition:color .2s}
.topbar nav a:hover,.topbar nav a.on{color:var(--ink)}
.topbar nav a.on{position:relative}
.topbar nav a.on::after{content:"";position:absolute;left:0;right:0;bottom:-6px;height:1px;background:var(--violet)}
/* Both exist in the markup on every page; only the phone breakpoint shows them. */
.burger{display:none}
.navpanel{display:none}
@media(max-width:760px){.topbar .in{padding:14px 18px;gap:14px}.topbar nav{gap:14px;font-size:11px}}

.shell{max-width:var(--maxw);margin:0 auto;padding:44px 32px 90px}
@media(max-width:760px){.shell{padding:28px 18px 64px}}

/* -------------------------------------------------------------- cards ----- */
.card{position:relative;border:1px solid var(--line);border-radius:18px;overflow:hidden;
  background:linear-gradient(165deg,rgba(30,20,45,.55),rgba(12,9,18,.35));
  backdrop-filter:blur(26px) saturate(1.3);
  box-shadow:0 40px 90px -40px rgba(124,58,237,.45),inset 0 1px 0 rgba(255,255,255,.05)}
.card::before{content:"";position:absolute;top:-1px;left:16%;right:16%;height:1px;
  background:linear-gradient(90deg,transparent,var(--violet),transparent)}
.card.flat{background:var(--panel);backdrop-filter:none;box-shadow:none}
.card.pad{padding:28px 30px}
@media(max-width:760px){.card.pad{padding:22px 20px}}

.link-card{display:block;transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .3s}
.link-card:hover{transform:translateY(-2px);border-color:rgba(180,150,240,.28);
  box-shadow:0 26px 60px -22px rgba(168,85,247,.55)}
/* cursor-follow spotlight; --mx/--my are set by portal.js */
.spot{position:relative}
.spot::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .3s;
  background:radial-gradient(240px circle at var(--mx,50%) var(--my,50%), rgba(168,85,247,.13), transparent 62%)}
.spot:hover::after{opacity:1}

/* ------------------------------------------------------------ buttons ----- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;border:none;border-radius:3px;
  padding:13px 24px;cursor:pointer;font-family:var(--disp);font-weight:600;font-size:14.5px;
  color:var(--on-violet);background:linear-gradient(100deg,var(--violet),var(--violet-3));
  position:relative;overflow:hidden;transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.btn:hover{transform:translateY(-2px);box-shadow:0 16px 40px -12px rgba(168,85,247,.72)}
.btn:disabled{opacity:.45;pointer-events:none}
.btn.sm{padding:9px 16px;font-size:13px}
.btn.block{width:100%}
.btn.load{pointer-events:none;color:transparent}
/* For the primary action of something irreversible: still the obvious button to
   press, but coloured so you cannot press it thinking it was the safe one. */
.btn.danger{background:linear-gradient(100deg,var(--hot),#ff7fc9);color:#2b0a1f;
  box-shadow:0 10px 30px -12px rgba(240,97,198,.75)}
.btn.danger:hover{box-shadow:0 16px 40px -12px rgba(240,97,198,.85)}
.btn.danger .sp{border-color:rgba(43,10,31,.35);border-top-color:#2b0a1f}
.btn .sp{display:none;position:absolute;inset:0;margin:auto;width:17px;height:17px;
  border:2px solid rgba(18,8,29,.35);border-top-color:var(--on-violet);border-radius:50%;animation:spin .7s linear infinite}
.btn.load .sp{display:block}
@keyframes spin{to{transform:rotate(360deg)}}

.ghost{display:inline-flex;align-items:center;justify-content:center;gap:8px;border:1px solid var(--line);
  border-radius:3px;padding:12px 20px;cursor:pointer;background:transparent;font-family:var(--mono);
  font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--body);transition:.25s}
.ghost:hover{border-color:var(--violet);color:var(--ink);background:rgba(168,85,247,.06)}
.ghost.sm{padding:8px 13px;font-size:10.5px}
.ghost.danger:hover{border-color:var(--bad);color:#ffd7e0;background:rgba(244,99,138,.08)}
.linkish{color:var(--violet-2);cursor:pointer;transition:.2s}
.linkish:hover{color:var(--lilac);text-decoration:underline;text-underline-offset:3px}

/* ------------------------------------------------------------- fields ----- */
.field{margin-bottom:18px}
.field label{font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);display:block;margin-bottom:9px}
.field .hint{font-size:12.5px;color:var(--faint);margin-top:7px;line-height:1.5}
.inp,.field input,.field textarea,.field select{
  width:100%;background:rgba(255,255,255,.03);border:1px solid var(--line-2);border-radius:10px;
  padding:13px 15px;color:var(--ink);font-family:var(--sans);font-weight:300;font-size:14.5px;
  transition:.25s;outline:none}
.field textarea{min-height:132px;resize:vertical;line-height:1.6}
.inp::placeholder,.field input::placeholder,.field textarea::placeholder{color:#584F68}
.inp:focus,.field input:focus,.field textarea:focus,.field select:focus{
  border-color:var(--violet);background:rgba(168,85,247,.06);box-shadow:0 0 0 4px rgba(168,85,247,.12)}
.field select{appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236E6480' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 14px center;padding-right:38px}
.field select option{background:var(--panel);color:var(--ink)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:640px){.row2{grid-template-columns:1fr}}

/* ------------------------------------------------------------- alerts ----- */
.msg{margin-top:18px;padding:13px 16px;border-radius:10px;font-size:13.5px;line-height:1.55;display:none}
.msg.ok{display:block;background:rgba(94,227,154,.08);border:1px solid rgba(94,227,154,.25);color:#A7F3CF}
.msg.err{display:block;background:rgba(244,63,94,.08);border:1px solid rgba(244,63,94,.28);color:#FCA5B5}
.msg.info{display:block;background:rgba(168,85,247,.08);border:1px solid rgba(168,85,247,.24);color:var(--lilac)}

/* -------------------------------------------------------------- pills ----- */
/* Paperclip marker for list rows: says a record arrived with files without
   having to open it. Quiet, because most rows have none. */
.clip{display:inline-flex;align-items:center;gap:4px;vertical-align:2px;margin-left:7px;
  padding:2px 7px 2px 5px;border-radius:100px;font-family:var(--mono);font-size:10.5px;
  color:var(--violet-2);background:rgba(168,85,247,.12);border:1px solid rgba(168,85,247,.24)}
.clip svg{width:11px;height:11px;flex:none}

.pill{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line-2);border-radius:100px;
  padding:5px 12px;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.pill.on{border-color:rgba(168,85,247,.4);color:var(--violet-2);background:rgba(168,85,247,.08)}
.pill.ok{border-color:rgba(94,227,154,.35);color:var(--ok);background:rgba(94,227,154,.07)}
.pill.warn{border-color:rgba(245,196,81,.35);color:var(--warn);background:rgba(245,196,81,.07)}
.pill.hot{border-color:rgba(240,97,198,.35);color:var(--hot);background:rgba(240,97,198,.07)}
.dot{width:6px;height:6px;border-radius:50%;background:currentColor;box-shadow:0 0 9px currentColor}
.dot.live{background:var(--ok);box-shadow:0 0 9px var(--ok);animation:bl 2.2s infinite}
@keyframes bl{0%,100%{opacity:1}50%{opacity:.3}}
.badge{display:inline-flex;align-items:center;justify-content:center;min-width:19px;height:19px;padding:0 6px;
  border-radius:100px;background:var(--violet);color:var(--on-violet);font-family:var(--mono);
  font-size:10.5px;font-weight:500;line-height:1}

/* --------------------------------------------------------- ring gauge ----- */
/* r=46 => circumference 289. Same geometry as the public status page. */
.ring{position:relative;width:132px;height:132px;flex:none}
.ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.ring .track{fill:none;stroke:rgba(180,150,240,.1);stroke-width:8}
.ring .fill{fill:none;stroke:url(#ringGrad);stroke-width:8;stroke-linecap:round;
  stroke-dasharray:289;stroke-dashoffset:289;transition:stroke-dashoffset 1.1s var(--ease)}
.ring .val{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
.ring .val b{font-family:var(--disp);font-size:31px;font-weight:600;color:var(--ink);letter-spacing:-.03em;line-height:1}
.ring .val span{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-top:5px}
.ring.sm{width:82px;height:82px}
.ring.sm .track,.ring.sm .fill{stroke-width:9}
.ring.sm .val b{font-size:20px}
.ring.sm .val span{font-size:8px;margin-top:3px}

/* --------------------------------------------------------- phase rail ----- */
.rail{display:flex;gap:5px;margin:0}
.rail .seg{flex:1;height:7px;border-radius:4px;background:var(--line-2);position:relative;overflow:hidden;
  transition:background .5s var(--ease)}
.rail .seg.done{background:linear-gradient(100deg,var(--violet),var(--violet-3))}
.rail .seg.now{background:linear-gradient(100deg,var(--violet),var(--violet-3));opacity:.55}
.rail .seg.now::after{content:"";position:absolute;top:0;left:-60%;width:45%;height:100%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.5),transparent);
  transform:skewX(-18deg);animation:sheen 2.6s infinite}
@keyframes sheen{0%{left:-60%}100%{left:130%}}
.rail-labels{display:flex;gap:5px;margin-top:9px}
.rail-labels span{flex:1;font-family:var(--mono);font-size:9px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--faint);text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rail-labels span.done{color:var(--lilac)}
.rail-labels span.now{color:var(--violet-2)}
@media(max-width:640px){.rail-labels span{font-size:0}.rail-labels span.now{font-size:9px;flex:3}}

/* ---------------------------------------------------- milestone rail ------ */
.mrail{position:relative;padding-left:34px;margin-top:4px}
.mrail::before{content:"";position:absolute;left:9px;top:8px;bottom:12px;width:2px;background:var(--line-2);border-radius:2px}
.mrail .spine{position:absolute;left:9px;top:8px;width:2px;border-radius:2px;
  background:linear-gradient(180deg,var(--violet),var(--violet-3));height:0;
  transition:height 1.2s var(--ease);box-shadow:0 0 12px rgba(168,85,247,.5)}
.ms{position:relative;padding:0 0 22px 0}
.ms:last-child{padding-bottom:0}
.ms .node{position:absolute;left:-34px;top:3px;width:20px;height:20px;border-radius:50%;
  border:2px solid var(--line);background:var(--bg);display:flex;align-items:center;justify-content:center;
  transition:.4s var(--ease)}
.ms .node i{width:7px;height:7px;border-radius:50%;background:transparent;display:block;transition:.3s}
.ms.done .node{border-color:var(--violet);background:var(--violet);box-shadow:0 0 16px rgba(168,85,247,.55)}
.ms.done .node i{background:var(--on-violet)}
.ms.active .node{border-color:var(--violet-2);animation:ping 2.4s infinite}
.ms.active .node i{background:var(--violet-2)}
.ms.blocked .node{border-color:var(--hot)}
.ms.blocked .node i{background:var(--hot)}
@keyframes ping{0%{box-shadow:0 0 0 0 rgba(192,132,252,.45)}70%{box-shadow:0 0 0 10px rgba(192,132,252,0)}100%{box-shadow:0 0 0 0 rgba(192,132,252,0)}}
.ms h4{font-family:var(--disp);font-size:15.5px;font-weight:500;color:var(--body);letter-spacing:-.01em;transition:color .3s}
.ms.done h4,.ms.active h4{color:var(--ink)}
.ms .meta{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin-top:5px}
.ms .detail{font-size:13.5px;color:var(--body);margin-top:7px;max-width:62ch}

/* ------------------------------------------------------------- thread ----- */
.thread{display:flex;flex-direction:column;gap:16px}
.bub{max-width:min(78%,620px);border-radius:14px;padding:14px 17px;border:1px solid var(--line-2);
  background:rgba(255,255,255,.025);position:relative}
.bub.mine{align-self:flex-end;background:rgba(168,85,247,.09);border-color:rgba(168,85,247,.22)}
.bub .who{font-family:var(--mono);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--faint);margin-bottom:7px;display:flex;gap:10px;align-items:center}
.bub .who b{color:var(--violet-2);font-weight:500}
.bub p{font-size:14.5px;color:var(--ink);white-space:pre-wrap;word-wrap:break-word;line-height:1.6}
.bub .atts{margin-top:11px;padding-top:11px;border-top:1px solid var(--line-2);display:flex;flex-wrap:wrap;gap:8px}
@media(max-width:640px){.bub{max-width:92%}}

/* -------------------------------------------------------------- files ----- */
.filelist{display:flex;flex-direction:column;gap:2px}
.file{display:flex;align-items:center;gap:14px;padding:12px 14px;border-radius:10px;transition:background .2s}
.file:hover{background:rgba(255,255,255,.03)}
.file .ic{width:34px;height:34px;flex:none;border-radius:8px;border:1px solid var(--line-2);
  display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:9px;
  letter-spacing:.04em;text-transform:uppercase;color:var(--violet-2);background:rgba(168,85,247,.06)}
.file .nm{flex:1;min-width:0}
.file .nm b{display:block;font-family:var(--sans);font-weight:400;font-size:14px;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.file .nm span{font-family:var(--mono);font-size:10.5px;color:var(--faint);letter-spacing:.05em}
.file .dir{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.chipfile{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line-2);border-radius:8px;
  padding:6px 11px;font-family:var(--mono);font-size:11px;color:var(--lilac);transition:.2s}
.chipfile:hover{border-color:var(--violet);color:var(--ink);background:rgba(168,85,247,.07)}

.drop{border:1.5px dashed var(--line);border-radius:14px;padding:26px;text-align:center;transition:.25s;cursor:pointer}
.drop:hover,.drop.over{border-color:var(--violet);background:rgba(168,85,247,.06)}
.drop .big{font-family:var(--disp);font-size:15px;color:var(--ink);margin-bottom:6px}
.drop .sub{font-size:12.5px;color:var(--faint)}
.uprow{display:flex;align-items:center;gap:12px;padding:9px 0;font-size:13px}
.uprow .bar{flex:1;height:5px;border-radius:3px;background:var(--line-2);overflow:hidden}
.uprow .bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--violet),var(--violet-2));transition:width .25s}

/* -------------------------------------------------------------- misc ------ */
.grid-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(340px,100%),1fr));gap:20px}
@media(max-width:700px){.grid-cards{grid-template-columns:1fr}}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;background:var(--line-2);
  border:1px solid var(--line-2);border-radius:14px;overflow:hidden}
.stats .cell{background:var(--bg);padding:20px 22px}
.stats .cell b{display:block;font-family:var(--disp);font-size:28px;font-weight:600;color:var(--ink);
  letter-spacing:-.03em;line-height:1;margin-bottom:7px}
.stats .cell span{font-family:var(--mono);font-size:10.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--faint)}
/* A cell that goes somewhere says so: the arrow only appears on a linked one. */
.stats a.cell{display:block;transition:background .25s var(--ease)}
.stats a.cell:hover{background:var(--panel-2)}
.stats a.cell span::after{content:" \2197";color:var(--violet-2);opacity:0;transition:opacity .2s}
.stats a.cell:hover span::after{opacity:1}

.sechead{display:flex;align-items:baseline;gap:16px;margin:0 0 20px}
.sechead h2{font-family:var(--disp);font-size:21px;font-weight:600;color:var(--ink);letter-spacing:-.02em}
.sechead .rule{flex:1;height:1px;background:var(--line-2)}

.empty{text-align:center;padding:52px 24px}
.empty svg{width:38px;height:38px;color:var(--faint);opacity:.5;margin-bottom:16px}
.empty h3{font-family:var(--disp);font-size:18px;color:var(--ink);font-weight:500;margin-bottom:8px}
.empty p{font-size:14px;color:var(--faint);max-width:44ch;margin:0 auto}

.skel{background:linear-gradient(90deg,rgba(180,150,240,.06),rgba(180,150,240,.13),rgba(180,150,240,.06));
  background-size:200% 100%;animation:sh 1.4s ease-in-out infinite;border-radius:8px}
@keyframes sh{0%{background-position:200% 0}100%{background-position:-200% 0}}

.reveal{opacity:0;transform:translateY(14px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.reveal.in{opacity:1;transform:none}

.tbl{width:100%;border-collapse:collapse}
.tbl th{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
  text-align:left;padding:0 14px 12px;border-bottom:1px solid var(--line-2);font-weight:400}
.tbl td{padding:14px;border-bottom:1px solid var(--line-2);font-size:14px;color:var(--body);vertical-align:middle}
.tbl tr:last-child td{border-bottom:none}
.tbl tbody tr{transition:background .2s}
.tbl tbody tr:hover{background:rgba(255,255,255,.025)}
.tbl td b{color:var(--ink);font-weight:500}

.backlink{font-family:var(--mono);font-size:12px;color:var(--faint);transition:.2s;display:inline-flex;gap:8px;align-items:center}
.backlink:hover{color:var(--ink)}

/*
 * Footer separators are drawn, not typed.
 *
 * They used to be literal <span>·</span> elements, which meant the mobile rule
 * had to hide them with :nth-child(even) and that quietly depended on every
 * footer having the same number of items in the same order. Adding a link
 * flipped the parity and started hiding real links instead. A ::before on every
 * item after the first cannot fall out of step.
 */
.foot{margin-top:64px;padding-top:26px;border-top:1px solid var(--line-2);
  font-family:var(--mono);font-size:11px;color:var(--faint);display:flex;gap:0;flex-wrap:wrap;align-items:center}
.foot > *{display:inline-flex;align-items:center}
.foot > * + *::before{content:"·";margin:0 12px;color:var(--line);flex:none}
.foot a{transition:color .2s}
.foot a:hover{color:var(--body)}

/* Thinking dots. Shared, because both the onboarding chat and the portal one
   need it and the wait is long enough that something has to acknowledge it. */
.typing{display:flex;gap:5px;padding:5px 0 3px}
.typing i{width:6px;height:6px;border-radius:50%;background:var(--violet-2);animation:ty 1.3s infinite}
.typing i:nth-child(2){animation-delay:.16s}
.typing i:nth-child(3){animation-delay:.32s}
@keyframes ty{0%,62%,100%{opacity:.22;transform:translateY(0)}31%{opacity:1;transform:translateY(-5px)}}
@media(prefers-reduced-motion:reduce){.typing i{animation:none;opacity:.5}}

/* ---------------------------------------------------------- sent ok ------ */
/* The end of a request, whichever way it was sent. Says what went, what happens
   next, and where to watch it, because "Sent." on its own leaves people
   wondering whether it worked. */
.sentcard{text-align:left;animation:sentIn .55s var(--ease) both}
@keyframes sentIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.sentcard .tick{width:60px;height:60px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;color:var(--ok);border:1.5px solid rgba(94,227,154,.45);
  box-shadow:0 0 44px -8px rgba(94,227,154,.5);margin-bottom:22px}
.sentcard .tick svg{width:27px;height:27px}
.sentcard h2{font-size:clamp(24px,3.4vw,30px);margin-bottom:10px}
.sentcard .lede{font-size:15.5px;line-height:1.65;color:var(--body);max-width:52ch}
.sent-items{margin-top:20px;display:flex;flex-direction:column;gap:8px}
.sent-item{display:flex;align-items:center;gap:11px;padding:13px 15px;border-radius:11px;
  background:rgba(255,255,255,.03);border:1px solid var(--line-2);
  font-size:14.5px;color:var(--ink)}
.sent-item .d{width:6px;height:6px;border-radius:50%;flex:none;background:var(--ok);
  box-shadow:0 0 9px var(--ok)}
.nexts{list-style:none;counter-reset:n;display:flex;flex-direction:column;gap:15px}
.nexts li{counter-increment:n;position:relative;padding-left:38px;
  font-size:14.5px;line-height:1.6;color:var(--body)}
.nexts li::before{content:counter(n);position:absolute;left:0;top:-1px;
  width:25px;height:25px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:11px;color:var(--violet-2);
  background:rgba(168,85,247,.12);border:1px solid rgba(168,85,247,.25)}
.nexts li b{color:var(--ink);font-weight:500}
.sent-acts{display:flex;gap:11px;flex-wrap:wrap;margin-top:28px}
@media(max-width:560px){
  .sent-acts{flex-direction:column}
  .sent-acts .btn,.sent-acts .ghost{width:100%;text-align:center;justify-content:center}
}

/* ------------------------------------------------------- chat sheet ------ */
/*
 * The assistant conversation inside the portal.
 *
 * On a phone it takes the whole screen, because a chat inside a normally
 * scrolling page is miserable once the keyboard is up: the composer disappears,
 * the thread jumps, and you scroll the page when you meant to scroll the
 * conversation. Full height with one internal scroller and a pinned composer is
 * the same shape the onboarding page uses, and it is the reason that one feels
 * right. On a desktop there is room, so it stays a card in the flow.
 */
.chatsheet{display:flex;flex-direction:column;min-height:0}
.chatsheet .cs-head{flex:none;display:flex;align-items:center;justify-content:space-between;
  gap:12px;margin-bottom:16px}
.chatsheet .cs-thread{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;display:flex;flex-direction:column;gap:14px;padding-bottom:4px}
.chatsheet .cs-foot{flex:none;padding-top:14px}
.chatsheet .cs-box{display:flex;gap:11px;align-items:flex-end}
/*
 * Styled explicitly rather than relying on .inp or .field textarea, because this
 * one sits in neither. Without these it fell through to the browser default and
 * rendered as a white box in the middle of a dark page.
 *
 * 16px is not a design choice: iOS Safari zooms the whole page whenever a
 * focused field is smaller than that.
 */
.chatsheet textarea{flex:1;min-height:52px;max-height:170px;resize:none;line-height:1.5;
  font-size:16px;border-radius:13px;padding:14px 16px;
  background:rgba(255,255,255,.03);border:1px solid var(--line-2);
  color:var(--ink);font-family:var(--sans);font-weight:300;
  outline:none;transition:.25s var(--ease)}
.chatsheet textarea::placeholder{color:#584F68}
.chatsheet textarea:focus{border-color:var(--violet);background:rgba(168,85,247,.06);
  box-shadow:0 0 0 4px rgba(168,85,247,.12)}
.chatsheet .cs-send{flex:none;height:52px;padding:0 20px}

@media(max-width:760px){
  /* full screen, tracking the real visible viewport rather than 100vh */
  .chatsheet.sheet{position:fixed;inset:0;z-index:60;height:var(--vh,100dvh);
    transform:translateY(var(--vv-top,0px));
    border-radius:0;border:0;margin:0;
    padding:calc(14px + env(safe-area-inset-top,0px)) 16px calc(12px + env(safe-area-inset-bottom,0px));
    /*
     * Fully opaque, and none of the card's translucency.
     *
     * .card is glassmorphic by design: a semi-transparent gradient over a
     * backdrop blur. Inherited here that let the page show through the chat.
     * The huge spread shadow paints the same colour past the panel's edges, so
     * the strip left over when the visible viewport is shorter than the layout
     * one is filled rather than showing the page underneath.
     */
    background:var(--bg);
    backdrop-filter:none;-webkit-backdrop-filter:none;
    box-shadow:0 0 0 100vmax var(--bg)}
  .chatsheet.sheet::before{display:none}   /* the card's lit top hairline */
  .chatsheet.sheet .cs-head{padding-bottom:12px;border-bottom:1px solid var(--line-2)}
  .chatsheet.sheet .cs-thread{padding-top:16px}
  .chatsheet.sheet .cs-foot{border-top:1px solid var(--line-2)}

  /*
   * The document is genuinely immobilised while the sheet is open, not merely
   * overflow:hidden. That matters for two reasons beyond stopping the page
   * sliding behind the panel:
   *
   *   - Nothing has to force the scroll position back, so the script stops
   *     fighting Safari. Calling scrollTo during the focus window was stopping
   *     the keyboard from coming up on the first tap at all.
   *   - Once the sheet closes the page is handed straight back its own
   *     scrolling, instead of a leftover handler snapping it to the top.
   *
   * The script records the scroll offset before locking and restores it after.
   */
  body.sheet-open{position:fixed;width:100%;overflow:hidden}
}

/* ----------------------------------------------------- mode picker ------- */
/* Shared by the onboarding page and the client request page: assistant or form,
   the visitor's call. */
.pick{display:flex;align-items:flex-start;gap:15px;width:100%;text-align:left;
  padding:18px;margin-bottom:12px;cursor:pointer;
  background:rgba(255,255,255,.025);border:1px solid var(--line-2);border-radius:15px;
  transition:border-color .25s var(--ease),background .25s var(--ease),transform .25s var(--ease)}
.pick:last-child{margin-bottom:0}
.pick:hover{border-color:rgba(168,85,247,.4);background:rgba(168,85,247,.06)}
.pick:active{transform:scale(.99)}
.pick .ic{flex:none;width:40px;height:40px;border-radius:11px;display:flex;align-items:center;
  justify-content:center;background:rgba(168,85,247,.13);color:var(--violet-2)}
.pick .ic svg{width:20px;height:20px}
.pick .tx{flex:1;min-width:0}
.pick .tx b{display:block;font-family:var(--disp);font-size:15.5px;font-weight:600;
  color:var(--ink);letter-spacing:-.01em;margin-bottom:5px}
.pick .tx em{display:block;font-style:normal;font-size:13.5px;line-height:1.55;color:var(--body)}
.pick .go{flex:none;align-self:center;color:var(--faint);font-size:17px;transition:.25s}
.pick:hover .go{color:var(--violet-2);transform:translateX(3px)}
.pick.load{opacity:.6;pointer-events:none}
@media(pointer:coarse){.pick .go{display:none}}

/* -------------------------------------------------------- nav badge ------ */
.navbadge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;
  padding:0 5px;margin-left:6px;border-radius:100px;background:var(--violet);color:var(--on-violet);
  font-family:var(--mono);font-size:9.5px;font-weight:500;line-height:1;vertical-align:1px}

/* ------------------------------------------------------ ticket card ------ */
/* Same card on the dashboard and the support page, so a ticket is one tap from
   either and looks like the same object in both places. */
.tcard{padding:20px 22px;display:flex;flex-direction:column;gap:12px}
.tcard .tt{display:flex;align-items:flex-start;gap:12px}
.tcard h3{flex:1;font-family:var(--disp);font-size:16.5px;font-weight:600;color:var(--ink);
  letter-spacing:-.015em;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden}
.tcard .tp{display:flex;gap:7px;flex-wrap:wrap}
.tcard .tm{font-family:var(--mono);font-size:10.5px;color:var(--faint);letter-spacing:.05em;
  padding-top:11px;border-top:1px solid var(--line-2)}

/* ------------------------------------------------------- tier strip ------ */
/* One line by default. The full comparison is a disclosure, because most of the
   time a client wants their tickets, not a pricing table. */
.tierbar{border:1px solid var(--line-2);border-radius:13px;background:rgba(168,85,247,.05)}
.tierbar > summary{list-style:none;cursor:pointer;padding:13px 18px;display:flex;align-items:center;
  gap:12px;font-size:14px;color:var(--body);flex-wrap:wrap}
.tierbar > summary::-webkit-details-marker{display:none}
.tierbar > summary b{color:var(--violet-2);font-weight:600;font-family:var(--disp)}
.tierbar > summary .more{margin-left:auto;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--faint);display:flex;align-items:center;gap:7px}
.tierbar > summary .more i{width:8px;height:8px;border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor;transform:rotate(45deg);transition:transform .25s var(--ease);
  display:block;margin-top:-3px}
.tierbar[open] > summary .more i{transform:rotate(-135deg);margin-top:2px}
.tierbar .inner{padding:2px 18px 18px}
@media(max-width:560px){.tierbar > summary .more span{display:none}}

/* ------------------------------------------------------ invoice card ---- */
.icard{padding:22px 24px;display:flex;flex-direction:column;gap:14px}
.icard .tt{display:flex;align-items:flex-start;gap:16px}
.icard .txt{flex:1;min-width:0}
.icard h3{font-family:var(--disp);font-size:16.5px;font-weight:600;color:var(--ink);
  letter-spacing:-.015em;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden}
.icard .sub{font-size:11px;color:var(--faint);letter-spacing:.05em;margin-top:6px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.icard .amt{font-family:var(--disp);font-size:23px;font-weight:600;color:var(--ink);
  letter-spacing:-.03em;white-space:nowrap;line-height:1.2}
.icard .tp{display:flex;gap:7px;flex-wrap:wrap}
@media(max-width:420px){.icard .amt{font-size:20px}}

/* --------------------------------------------------------- archive ------ */
/* Finished work still matters but it should not be the first thing you scroll
   past, so it collapses and lists as rows rather than cards. This is what keeps
   a page usable after twenty projects, or twenty tickets, instead of two.
   Shared, because the dashboard and the support page both need it. */
.arch{margin-top:16px;border:1px solid var(--line-2);border-radius:14px;background:rgba(255,255,255,.012)}
.arch > summary{list-style:none;cursor:pointer;padding:16px 22px;display:flex;align-items:center;gap:12px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
  transition:color .2s;border-radius:14px}
.arch > summary::-webkit-details-marker{display:none}
.arch > summary:hover{color:var(--ink)}
.arch > summary .chev{width:9px;height:9px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(-45deg);transition:transform .25s var(--ease);margin-left:-2px}
.arch[open] > summary .chev{transform:rotate(45deg)}
.arch > summary .n{margin-left:auto;font-family:var(--disp);font-size:14px;letter-spacing:0;color:var(--body)}
.arch .inner{padding:2px 22px 14px}
.crow{display:flex;align-items:center;gap:14px;padding:13px 0;border-top:1px solid var(--line-2);
  font-size:14px;color:var(--body);transition:.2s}
.crow:hover{padding-left:5px;color:var(--ink)}
.crow .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}
.crow .when{font-family:var(--mono);font-size:10.5px;color:var(--faint);white-space:nowrap}
.crow .note{flex:2;min-width:0;font-size:12.5px;color:var(--faint);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
@media(max-width:700px){.crow .note{display:none}}

/* ------------------------------------------------------------ modal ----- */
/* Used for anything irreversible. The browser's own confirm() is a grey system
   box that says nothing about consequences, and on iOS it names the site, which
   makes a deliberate action look like a popup. */
.modalwrap{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;
  padding:20px;opacity:0;transition:opacity .22s var(--ease)}
.modalwrap.in{opacity:1}
.modalwrap .veil{position:absolute;inset:0;background:rgba(6,4,10,.72);backdrop-filter:blur(7px);
  -webkit-backdrop-filter:blur(7px)}
.modalwrap .modal{position:relative;width:100%;max-width:440px;border:1px solid var(--line-2);border-radius:18px;
  background:linear-gradient(165deg,#191223,#100b1a);padding:28px 30px 24px;
  box-shadow:0 34px 90px -28px rgba(0,0,0,.9),0 0 0 1px rgba(168,85,247,.07);
  transform:translateY(10px) scale(.985);transition:transform .28s var(--ease)}
.modalwrap.in .modal{transform:none}
.modalwrap .modal::before{content:"";position:absolute;inset:0 0 auto;height:1px;border-radius:18px 18px 0 0;
  background:linear-gradient(90deg,transparent,rgba(168,85,247,.5),transparent)}
.modalwrap .modal .ic{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;
  background:rgba(168,85,247,.12);border:1px solid rgba(168,85,247,.25);margin-bottom:17px}
.modalwrap .modal .ic svg{width:20px;height:20px;stroke:var(--violet-2);stroke-width:1.6;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
.modalwrap .modal.danger .ic{background:rgba(240,97,198,.11);border-color:rgba(240,97,198,.28)}
.modalwrap .modal.danger .ic svg{stroke:var(--hot)}
.modalwrap .modal h3{font-family:var(--disp);font-size:20px;font-weight:600;color:var(--ink);letter-spacing:-.02em;
  line-height:1.3;margin-bottom:9px}
.modalwrap .modal p{font-size:14px;color:var(--body);line-height:1.65}
.modalwrap .modal .note{margin-top:13px;padding:12px 14px;border-radius:10px;border:1px solid var(--line-2);
  background:rgba(255,255,255,.02);font-size:12.5px;color:var(--faint);line-height:1.6}
.modalwrap .modal .btns{display:flex;gap:11px;margin-top:22px;justify-content:flex-end}
.modalwrap .modal .btns .btn, .modalwrap .modal .btns .ghost{min-width:112px;justify-content:center}
@media(max-width:460px){
  /* On a phone the buttons stack, primary action on top where the thumb is. */
  .modalwrap .modal{padding:24px 22px 20px}
  .modalwrap .modal .btns{flex-direction:column-reverse;gap:9px}
  .modalwrap .modal .btns .btn, .modalwrap .modal .btns .ghost{width:100%}
}
@media (prefers-reduced-motion: reduce){
  .modalwrap, .modalwrap .modal{transition:none}
}

/* ------------------------------------------------------- section bar ---- */
/* A heading with a rule and an action on the end. On a phone the action drops
   under the heading rather than squeezing the title to two characters. */
.secbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:18px}
.secbar h2{font-family:var(--disp);font-size:20px;font-weight:600;color:var(--ink);letter-spacing:-.02em}
.secbar .rule{flex:1;height:1px;background:var(--line-2);min-width:20px}
@media(max-width:460px){
  .secbar .rule{display:none}
  .secbar > .btn, .secbar > .ghost{width:100%}
}

/* ============================================================ mobile ======
   Phones are the common case for a client checking in, not an afterthought.
   Everything below fixes something that actually breaks on a real handset.
   ========================================================================= */

/* Nothing may drag the page sideways. If a child is too wide it scrolls inside
   its own box, never by shifting the whole document.

   clip rather than hidden: hidden makes the element a scroll container, and with
   it set on *both* html and body iOS Safari ends up with two of them and cannot
   decide which owns the vertical scroll, so the page fights you or springs back.
   clip suppresses the overflow without creating a scroller. The hidden line is
   the fallback for anything that does not know clip. */
html, body { overflow-x: hidden; max-width: 100%; }
html, body { overflow-x: clip; }
.page { overflow-x: clip; }

@media (max-width:760px){
  /*
   * Hamburger, not a scroller.
   *
   * Six destinations do not fit on a 375px line. They were a horizontal strip,
   * which meant targets far under the 44px minimum and a bar that always started
   * scrolled left, so the page you were actually on was usually off screen. A
   * panel gives every item a full-width row and marks the current one.
   */
  .topbar .in{flex-direction:row;align-items:center;flex-wrap:nowrap;
    justify-content:space-between;gap:14px;padding:12px 16px}
  .topbar .in > a:first-child{flex:none}
  .topbar nav{display:none}
  .burger{display:flex;flex:none;flex-direction:column;justify-content:center;gap:5px;
    width:44px;height:44px;margin-right:-10px;padding:0 10px;
    background:none;border:0;cursor:pointer;-webkit-tap-highlight-color:transparent}
  .burger span{display:block;height:1.5px;width:22px;border-radius:2px;background:var(--body);
    transition:transform .32s var(--ease),opacity .2s ease}
  .topbar.menu .burger span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
  .topbar.menu .burger span:nth-child(2){opacity:0}
  .topbar.menu .burger span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}

  .navpanel{display:block;overflow:hidden;max-height:0;
    border-top:1px solid transparent;
    transition:max-height .38s var(--ease),border-color .38s ease}
  .topbar.menu .navpanel{max-height:70vh;border-top-color:var(--line-2);overflow-y:auto}
  .navpanel a{display:flex;align-items:center;gap:10px;
    padding:0 20px;min-height:54px;
    font-family:var(--mono);font-size:13px;letter-spacing:.04em;color:var(--body);
    border-bottom:1px solid var(--line-2);transition:background .2s,color .2s}
  .navpanel a:last-child{border-bottom:none;color:var(--faint)}
  .navpanel a:active{background:rgba(168,85,247,.09)}
  .navpanel a.on{color:var(--ink);background:rgba(168,85,247,.07);
    box-shadow:inset 3px 0 0 var(--violet)}
  .navpanel a.on::after{display:none}     /* the desktop underline has no place here */
  .navpanel .navbadge{margin-left:auto}

  .shell{padding:22px 16px 56px}
  .card.pad{padding:20px 18px}
  .sechead{flex-wrap:wrap;gap:10px}
  .stats{grid-template-columns:repeat(auto-fit,minmax(128px,1fr))}
  .stats .cell{padding:16px 16px}
  .stats .cell b{font-size:23px}
  /* Wrap instead of stacking, so five short links take two lines rather than
     five. Separators are drawn by CSS (see .foot below), not by literal spans,
     so there is no parity trick to break when a link is added. */
  .foot{flex-wrap:wrap;gap:6px 0;align-items:flex-start}
}

/* Wide content scrolls inside itself. A six-column table has no business
   resizing a phone screen. */
@media (max-width:900px){
  .scrollx{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:thin;margin:0 -2px;padding:0 2px}
  .scrollx > .tbl{min-width:640px}
  .tbl td, .tbl th{white-space:nowrap}
  .tbl td b{white-space:normal}
}

/*
 * iOS Safari zooms the whole page whenever a focused input has a font-size
 * under 16px, which then leaves the layout scrolled sideways. Coarse pointers
 * get 16px inputs and 44px tap targets, which is Apple's documented minimum.
 */
@media (pointer:coarse){
  .inp, .field input, .field textarea, .field select{font-size:16px}
  .btn{min-height:46px;padding:12px 22px}
  .ghost{min-height:44px}
  .ghost.sm{min-height:36px;padding:8px 14px}
  .topbar nav a{padding:6px 0}
  .file .ghost.sm, .filelist .ghost.sm{min-height:34px}
  /* hover-only affordances do nothing on touch and just cause sticky states */
  .link-card:hover{transform:none}
  .crow:hover, .tk:hover, .file:hover{padding-left:0}
  .spot::after{display:none}
}

/* Notched and rounded screens: keep content clear of the inset areas. */
@supports (padding:max(0px)){
  @media (max-width:760px){
    .shell{padding-left:max(16px, env(safe-area-inset-left));
           padding-right:max(16px, env(safe-area-inset-right))}
    .topbar .in{padding-left:max(16px, env(safe-area-inset-left));
                padding-right:max(16px, env(safe-area-inset-right))}
    body{padding-bottom:env(safe-area-inset-bottom)}
  }
}

/* Long unbroken strings (URLs, invoice numbers, emails) must not set the width. */
.bub p, .crow .nm, .file .nm b, .tk h4, .scope{overflow-wrap:anywhere}

/* ---------------------------------------------------- reduced motion ------ */
@media(prefers-reduced-motion:reduce){
  .blob,.rail .seg.now::after,.dot.live,.ms.active .node,.skel{animation:none}
  .reveal{opacity:1;transform:none;transition:none}
  .ring .fill,.mrail .spine{transition:none}
  *{scroll-behavior:auto!important}
}

/* Cloudflare Turnstile container.
   Collapsed until a widget actually renders, so a page with Turnstile
   unconfigured (or the script blocked) shows no gap where nothing is. */
.tsbox{margin:0}
.tsbox.ts-on{margin:0 0 18px}
.tsbox iframe{max-width:100%}

/* ============================================================================
   Searchable select.

   Progressive enhancement over a real <select>, which stays in the DOM as the
   source of truth: the value, form submission and change events are all still
   the native element's, so the sixty-odd places that read `.value` keep working
   untouched. If this CSS or its JS never loads, every dropdown is still a
   perfectly good native select.
   ========================================================================= */
.ss{position:relative;width:100%}
.ss select.ss-native{position:absolute;opacity:0;pointer-events:none;width:1px;height:1px;
  margin:0;padding:0;border:0;clip:rect(0 0 0 0)}

/* the closed control, matching .field input so mixed forms line up */
.ss-btn{display:flex;align-items:center;gap:10px;width:100%;text-align:left;cursor:pointer;
  background:rgba(255,255,255,.03);border:1px solid var(--line-2);border-radius:11px;
  padding:12px 14px;color:var(--ink);font-family:var(--sans);font-size:15px;
  transition:border-color .22s,background .22s,box-shadow .22s}
.ss-btn:hover{border-color:rgba(180,150,240,.28)}
.ss-btn:focus-visible{outline:none;border-color:var(--violet);
  background:rgba(168,85,247,.06);box-shadow:0 0 0 4px rgba(168,85,247,.12)}
.ss.open .ss-btn{border-color:var(--violet);background:rgba(168,85,247,.06);
  box-shadow:0 0 0 4px rgba(168,85,247,.12)}
.ss-val{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ss-val.ph{color:var(--faint)}
.ss-caret{flex:none;width:12px;height:12px;color:var(--faint);transition:transform .24s var(--ease)}
.ss.open .ss-caret{transform:rotate(180deg);color:var(--violet-2)}

/* the panel: this is the glass */
.ss-panel{position:absolute;z-index:60;left:0;right:0;top:calc(100% + 7px);
  border:1px solid var(--line);border-radius:13px;overflow:hidden;
  background:linear-gradient(168deg,rgba(30,21,44,.93),rgba(14,10,21,.93));
  backdrop-filter:blur(22px) saturate(1.35);-webkit-backdrop-filter:blur(22px) saturate(1.35);
  box-shadow:0 30px 70px -28px rgba(0,0,0,.85),0 0 0 1px rgba(255,255,255,.03) inset;
  animation:ssIn .18s var(--ease)}
.ss-panel[hidden]{display:none}
.ss-panel.up{top:auto;bottom:calc(100% + 7px)}
@keyframes ssIn{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.ss-panel{animation:none}}
.ss-panel::before{content:"";position:absolute;top:0;left:14%;right:14%;height:1px;
  background:linear-gradient(90deg,transparent,var(--violet),transparent)}

.ss-search{display:flex;align-items:center;gap:9px;padding:11px 13px;
  border-bottom:1px solid var(--line-2)}
.ss-search svg{width:14px;height:14px;color:var(--faint);flex:none}
.ss-search input{flex:1;min-width:0;background:none;border:none;outline:none;color:var(--ink);
  font-family:var(--sans);font-size:15px}   /* 15px+: smaller zooms iOS on focus */
.ss-search input::placeholder{color:var(--faint)}

.ss-list{max-height:264px;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:6px}
.ss-opt{display:flex;align-items:center;gap:10px;padding:10px 11px;border-radius:9px;
  cursor:pointer;font-size:14.5px;color:var(--body);line-height:1.35}
.ss-opt:hover,.ss-opt.cur{background:rgba(168,85,247,.13);color:var(--ink)}
.ss-opt[aria-selected="true"]{color:var(--ink)}
.ss-opt .ss-tick{flex:none;width:14px;height:14px;color:var(--violet-2);opacity:0}
.ss-opt[aria-selected="true"] .ss-tick{opacity:1}
.ss-opt .ss-lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* an option can carry a quiet second line, e.g. a project's phase */
.ss-opt .ss-sub{display:block;font-family:var(--mono);font-size:10.5px;color:var(--faint);
  margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ss-none{padding:16px 12px;text-align:center;font-size:13.5px;color:var(--faint)}

@media(max-width:560px){
  /* On a phone the panel becomes a sheet: an absolutely positioned dropdown near
     the bottom of a short screen ends up half off it, and the keyboard covers
     whatever is left. */
  .ss.open .ss-panel{position:fixed;left:0;right:0;bottom:0;top:auto;
    border-radius:18px 18px 0 0;max-height:78dvh;animation:ssSheet .3s var(--ease)}
  @keyframes ssSheet{from{transform:translateY(100%)}to{transform:none}}
  .ss.open .ss-panel.up{top:auto;bottom:0}
  .ss-list{max-height:calc(78dvh - 60px)}
  .ss-opt{padding:13px 12px;font-size:15px}   /* thumbs, not cursors */
  .ss-backdrop{position:fixed;inset:0;z-index:59;background:rgba(6,4,10,.6);
    backdrop-filter:blur(3px)}
}

/* Files queued against a comment, before Send. Chips rather than a list: they
   are provisional, and a chip reads as something you can still take back. */
.msgpicked{display:flex;flex-wrap:wrap;gap:7px;margin:10px 0 0}
.msgpicked:empty{margin:0}
.mchip{display:inline-flex;align-items:center;gap:7px;padding:5px 6px 5px 11px;border-radius:100px;
  font-size:12.5px;color:var(--ink);background:rgba(168,85,247,.1);
  border:1px solid rgba(168,85,247,.22);max-width:100%}
.mchip b{font-size:10.5px;color:var(--faint);font-weight:400}
.mchip button{width:19px;height:19px;flex:none;border:none;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.06);color:var(--faint);font-size:14px;line-height:1;
  display:flex;align-items:center;justify-content:center;transition:.18s}
.mchip button:hover{background:rgba(244,99,138,.18);color:#f4638a}

/* Admin previewing a client page. Deliberately loud: the page is a faithful
   copy of the client's view, so the only thing telling you it is not yours is
   this bar. Composers stay visible but inert, because hiding them would change
   the very layout you came here to check. */
.previewbar{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin:0 0 20px;
  padding:12px 16px;border-radius:10px;font-size:13px;color:var(--ink);
  background:linear-gradient(100deg,rgba(245,158,11,.16),rgba(245,158,11,.05));
  border:1px solid rgba(245,158,11,.34)}
.previewbar b{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:#fbbf24}
.previewbar span:not(.pv-dot){color:var(--body);flex:1 1 260px;min-width:0;line-height:1.5}
.previewbar a{margin-left:auto;flex:none;color:#fbbf24;font-size:12.5px;white-space:nowrap}
.previewbar a:hover{text-decoration:underline}
.pv-dot{width:8px;height:8px;flex:none;border-radius:50%;background:#fbbf24;
  box-shadow:0 0 0 3px rgba(245,158,11,.2)}
.is-preview #send,.is-preview #msg,.is-preview #msgAttach{opacity:.5;pointer-events:none}

/* Phase guide under the "Where we are" rail.

   One phase at a time by default. The rail above is the picker, and the
   expander opens the full sequence for anyone who wants to read ahead or look
   back at what is already done. */
.pguide{margin-top:20px;padding-top:18px;border-top:1px solid var(--line-2)}
.pg-card{display:flex;gap:13px;align-items:flex-start;padding:14px 15px;border-radius:10px;
  border:1px solid var(--line-2);background:rgba(255,255,255,.015)}
.pg-n{flex:none;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;font-family:var(--mono);font-size:11px;margin-top:1px;
  border:1px solid var(--line);color:var(--faint);background:rgba(255,255,255,.02)}
.pg-b{min-width:0}
.pg-b b{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:14.5px;
  font-weight:600;color:var(--ink);margin-bottom:4px}
.pg-b em{font-family:var(--mono);font-style:normal;font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase;padding:3px 8px;border-radius:100px;color:var(--faint);
  background:rgba(255,255,255,.04);border:1px solid var(--line-2)}
.pg-b p{margin:0;font-size:13.5px;line-height:1.62;color:var(--body)}

.pg-card.now{background:linear-gradient(100deg,rgba(168,85,247,.12),rgba(168,85,247,.03));
  border-color:rgba(168,85,247,.3)}
.pg-card.now .pg-n{border-color:var(--violet);color:#fff;background:var(--violet)}
.pg-card.now .pg-b em{color:var(--violet-2);background:rgba(168,85,247,.14);
  border-color:rgba(168,85,247,.32)}
.pg-card.done .pg-n{border-color:rgba(168,85,247,.42);color:var(--lilac);background:rgba(168,85,247,.1)}
.pg-card.next{opacity:.72}

/* expanded: the whole sequence, current one still obvious */
.pg-all{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.pg-all .pg-card{width:100%}

.pg-more{display:inline-flex;align-items:center;gap:8px;margin-top:12px;padding:8px 13px;
  background:none;border:1px solid var(--line-2);border-radius:8px;cursor:pointer;
  font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);transition:.22s}
.pg-more:hover{border-color:rgba(168,85,247,.4);color:var(--lilac)}
.pg-chev{width:7px;height:7px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .25s}
.pg-more.open .pg-chev{transform:rotate(-135deg) translate(-2px,-2px)}

/* the rail labels become the picker */
.rail-labels span{cursor:pointer;transition:color .2s,opacity .2s;border-radius:4px}
.rail-labels span:hover{color:var(--lilac)}
.rail-labels span.sel{color:var(--violet-2);text-decoration:underline;text-underline-offset:3px}
.rail-labels span:focus-visible{outline:1px solid var(--violet);outline-offset:3px}

@media(max-width:520px){
  .pg-card{padding:12px 13px;gap:11px}
  .pg-b p{font-size:13px}
}

/* Why the composer is dead in an admin preview. Sits with the composer, not at
   the top of the page: by the time you reach Feedback the banner is scrolled
   away, and a greyed-out box with no reason reads as a bug. */
.pv-note{margin-top:10px;padding:10px 13px;border-radius:8px;font-size:12.5px;line-height:1.55;
  color:var(--body);background:rgba(245,158,11,.08);border:1px solid rgba(245,158,11,.26)}
.pv-note a{color:#fbbf24}
.pv-note a:hover{text-decoration:underline}

/* Checkbox rows in admin forms. Label-wrapped so the whole row is the hit
   target — these get used on a phone while something is actually broken.

   The selectors have to out-specify `.field label` and `.field input`, which
   otherwise turn each label into an uppercase mono block and each checkbox
   into a full-width text input. That is exactly what happened first time. */
.cbxs{display:flex;flex-direction:column;gap:2px;margin-top:4px}
label.cbx{display:flex;align-items:center;gap:10px;font-family:var(--sans);
  font-size:13.5px;font-weight:400;letter-spacing:0;text-transform:none;
  color:var(--body);cursor:pointer;padding:6px 0;margin:0;user-select:none}
.field label.cbx{font-family:var(--sans);font-size:13.5px;letter-spacing:0;
  text-transform:none;color:var(--body);margin:0;display:flex}
.cbx input[type=checkbox],
.field .cbx input[type=checkbox]{appearance:auto;-webkit-appearance:checkbox;
  accent-color:var(--violet);width:16px;height:16px;min-width:16px;flex:none;
  padding:0;margin:0;border:0;border-radius:0;background:none;box-shadow:none;cursor:pointer}
.cbx input[type=checkbox]:disabled{cursor:not-allowed;opacity:.5}
.cbx:has(input:disabled){opacity:.55;cursor:not-allowed}

/* Close affordance for guarded dialogs, which no longer shut on a backdrop click. */
.dlgx{position:absolute;top:12px;right:14px;width:30px;height:30px;border:none;
  border-radius:50%;cursor:pointer;background:rgba(255,255,255,.05);color:var(--faint);
  font-size:20px;line-height:1;display:flex;align-items:center;justify-content:center;
  transition:.2s;z-index:2}
.dlgx:hover{background:rgba(244,99,138,.18);color:#f4638a}
.card.modal{position:relative}
