/* GayStash — modern app shell. Hand-written CSS on custom properties.
   Register: Apple's precision + Spotify's persistent shell + the density
   a tube-site audience expects. Single type family, weight and scale doing
   the hierarchy; one accent for action, one for caution, nothing else. */

:root{
  --bg:#0a0a0c;          /* page */
  --surface:#131318;     /* cards, panels */
  --surface-2:#1c1c23;   /* hover / raised */
  --surface-3:#26262f;   /* pressed, inputs */
  --line:#26262f;        /* hairlines */
  --line-2:#34343f;      /* stronger dividers */

  --text:#f5f5f7;        /* primary */
  --text-2:#a1a1aa;      /* secondary — 7.4:1 on bg */
  --text-3:#7c7c88;      /* tertiary — 4.6:1 on bg */

  --brand:#e14f62;       /* the logo mark, sampled from the brand sheet */
  --accent:#ff2f6e;      /* action and active state — deliberately hotter */
  --accent-hi:#ff5488;   /* accent hover */
  --accent-ink:#ffffff;  /* text on accent */
  --warn:#ffb020;        /* caution — ad-heavy hosts, and nothing else */
  --warn-ink:#1a1200;
  --live:#35d07f;        /* here right now — presence, and nothing else */

  /* thumbnails stand in for real stills */
  --t1:#3f5b8f; --t2:#8f3f5b; --t3:#3f8f7a; --t4:#8f6a3f;
  --t5:#5b3f8f; --t6:#8f453f; --t7:#3f7a8f; --t8:#5c8f3f;

  --r-sm:8px; --r-md:12px; --r-lg:16px; --r-pill:999px;
  --ease:cubic-bezier(.2,.7,.2,1);
  --side-w:236px;

  --font:"Plus Jakarta Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;
}

*,*::before,*::after{box-sizing:border-box}
html{color-scheme:dark;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--text);
  font:500 15px/1.5 var(--font);
  -webkit-font-smoothing:antialiased;
  font-feature-settings:"tnum" 1;
}
img,svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font:inherit;color:inherit}
button{cursor:pointer;border:0;background:none}
h1,h2,h3{margin:0;font-weight:800;letter-spacing:-.022em}
p{margin:0}
[hidden]{display:none !important}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
::selection{background:var(--accent);color:var(--accent-ink)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.icon{width:18px;height:18px;flex:none;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

/* ── controls ───────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  height:38px;padding:0 16px;border-radius:var(--r-pill);
  background:var(--surface-2);color:var(--text);
  font-size:14px;font-weight:700;letter-spacing:-.01em;white-space:nowrap;
  transition:background .14s var(--ease),color .14s var(--ease),opacity .14s var(--ease);
}
.btn:hover{background:var(--surface-3)}
.btn-accent{background:var(--accent);color:var(--accent-ink)}
.btn-accent:hover{background:var(--accent-hi)}
.btn-ghost{background:transparent}
.btn-ghost:hover{background:var(--surface-2)}
.btn-lg{height:46px;padding:0 22px;font-size:15px}
.btn-sm{height:32px;padding:0 13px;font-size:13px}
/* A button variant, so it lives with the buttons. It was defined in two page
   sheets — same failure as .ctrl and the .a1–.a8 palette: whichever page
   forgot to load the right sheet got an ordinary grey button on the one
   control that needed to look different. */
.btn-danger{background:transparent;color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.btn-danger:hover{background:var(--accent);color:var(--accent-ink)}
.btn[aria-pressed=true]{background:var(--accent);color:var(--accent-ink)}
.btn[aria-pressed=true]:hover{background:var(--accent-hi)}
.btn-accent[aria-pressed=true]{background:var(--surface-2);color:var(--text)}
.btn:disabled{opacity:.4;pointer-events:none}
.count{font-variant-numeric:tabular-nums;opacity:.8}

.pill{
  display:inline-flex;align-items:center;gap:6px;height:34px;padding:0 14px;
  border-radius:var(--r-pill);background:var(--surface-2);
  font-size:13.5px;font-weight:600;color:var(--text-2);white-space:nowrap;
  transition:background .14s var(--ease),color .14s var(--ease);
}
.pill:hover{background:var(--surface-3);color:var(--text)}
.pill.on{background:var(--text);color:var(--bg)}

/* ── app shell ──────────────────────────────────────────────────── */
/* The top bar belongs to the shell, not to the content column: on a phone
   that puts search above the chip rail, which is the order a thumb expects
   and the order every touch app uses. */
.app{
  display:grid;grid-template-columns:minmax(0,1fr);min-height:100vh;
  /* rows size to their content. Without this, auto rows stretch to fill the
     viewport on any page shorter than the screen — which put the search row
     floating in the middle of a 170px header and the rail's divider hanging
     in mid-air. */
  align-content:start;
}
/* small screens: the sidebar lays itself out as a horizontal pill rail,
   so the categories exist once in the DOM rather than twice */
.side{
  display:flex;gap:8px;overflow-x:auto;padding:12px 14px;
  scrollbar-width:none;-ms-overflow-style:none;
  overscroll-behavior-x:contain;scroll-snap-type:x proximity;
  /* the row runs off the edge, so say so: the last chip fades rather than
     ending flush, which is the only cue a touch user gets that it moves */
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 44px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 44px),transparent);
}
.side-nav a,.side-cats a{scroll-snap-align:start}
.side::-webkit-scrollbar{display:none}
.side .brand,.side-lab,.side-foot{display:none}
.side-nav,.side-cats{display:contents}
.side-nav a,.side-cats a{
  display:inline-flex;align-items:center;height:34px;padding:0 14px;flex:none;
  border-radius:var(--r-pill);background:var(--surface-2);
  font-size:13.5px;font-weight:600;color:var(--text-2);white-space:nowrap;
  transition:background .14s var(--ease),color .14s var(--ease);
}
.side-nav a:hover,.side-cats a:hover{background:var(--surface-3);color:var(--text)}
.side-nav a.on,.side-cats a.on{background:var(--text);color:var(--bg)}
.side-nav .icon{display:none}

.top{
  position:sticky;top:0;z-index:20;
  display:flex;align-items:center;gap:10px;
  padding:10px 14px;background:var(--bg);
  border-bottom:1px solid var(--line);
}
/* "Stack" — three bars, a pile of things kept. No play triangle: nothing
   streams from here, and the old mark was the last place still claiming it
   did. Sized in em against the wordmark rather than in pixels: the mark sits
   at 0.95x the type size and the gap at .34em, so the lockup holds at 17px on
   a phone and 21px in the desktop sidebar without being respecified. Sizing
   it in px was what made the old pairing read as two objects on a line. */
.brand{display:flex;align-items:center;gap:.34em;font-size:19px;font-weight:800;letter-spacing:-.03em;flex:none}
.mark{width:.95em;height:.95em;flex:none;fill:var(--brand);position:relative;top:.03em}
.mark-mono{fill:currentColor}
.search{position:relative;flex:1 1 auto;min-width:0;display:flex;align-items:center}
.search .icon{position:absolute;left:12px;color:var(--text-3);pointer-events:none}
.search input{
  width:100%;min-width:0;height:40px;padding:0 14px 0 38px;
  background:var(--surface);border:1px solid transparent;border-radius:var(--r-pill);
  font-size:14.5px;font-weight:500;color:var(--text);
  transition:background .14s var(--ease),border-color .14s var(--ease);
}
.search input::placeholder{color:var(--text-3)}
.search input:focus{background:var(--surface-2);border-color:var(--line-2);outline:none}
.search input:focus-visible{border-color:var(--accent)}
.top-acts{display:flex;align-items:center;gap:8px;flex:none}

/* ── the account control ────────────────────────────────────────
   Signed in, the right of the bar becomes: bell, Stash a link, you. The
   avatar is the one glyph on this site that beats a word — identity is
   found by face faster than by name, and it is the only thing in the bar
   that is about you rather than about the archive. */
.acct{position:relative;flex:none}
.acct-btn{
  display:flex;align-items:center;gap:8px;height:38px;padding:0 4px;
  border-radius:var(--r-pill);background:none;
  font-size:14px;font-weight:700;color:var(--text-2);
  transition:background .14s var(--ease);
}
.acct-btn:hover,.acct-btn[aria-expanded=true]{background:var(--surface-2);color:var(--text)}
.acct-btn .av{width:30px;height:30px;font-size:12.5px}
/* Both hidden until there is room. The name goes first for the obvious
   reason; the chevron goes with it because a chevron next to a bare disc is
   pointing at nothing — with no label beside it the avatar is already the
   whole affordance, and aria-haspopup carries the meaning that the glyph
   was carrying. That is ~20px back for the search field. */
.acct-name,.acct-chev{display:none}
.acct-chev{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;margin-right:4px}
.acct-btn[aria-expanded=true] .acct-chev{transform:rotate(180deg)}

.acct-menu{
  position:absolute;right:0;top:calc(100% + 8px);z-index:40;width:250px;
  background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-md);overflow:hidden;
}
/* who you are signed in as, said outright. On a site where people run more
   than one account, "which one am I?" is the question this answers. */
.acct-me{display:flex;align-items:center;gap:10px;padding:13px;min-width:0}
.acct-me:hover{background:var(--surface-2)}
.acct-me .av{width:36px;height:36px;font-size:14px}
.acct-me > span:not(.av){min-width:0}
.acct-me strong{display:block;font-size:14.5px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The sub-line only, reached by path rather than by `span:not(.av)`. A bare
   descendant rule also catches the wrapper and the .dot inside it, which
   turned the separator into a line of its own — the third time this exact
   shape has bitten, after .prow-b and .crow-b. */
.acct-me > span > span{
  display:block;padding-top:1px;
  font-size:12px;font-weight:500;color:var(--text-3);
}

.acct-grp{border-top:1px solid var(--line);padding:5px 0}
.acct-grp a{
  display:flex;align-items:center;gap:10px;
  padding:9px 13px;font-size:13.5px;font-weight:600;color:var(--text-2);
}
.acct-grp a:hover{background:var(--surface-2);color:var(--text)}
.acct-n{margin-left:auto;font-size:12.5px;font-weight:700;color:var(--text-3);font-variant-numeric:tabular-nums}
.acct-n.warn{color:var(--warn)}

/* The bell is part of the topbar partial, so it belongs to the shell. It used
   to live in modern-notifications.css, which 13 of the pages carrying a bell
   do not load — the count rendered as loose text beside the icon on all of
   them. Same failure as .ctrl and the .a1–.a8 avatar palette: shared chrome
   parked in a page sheet. */
.bell{position:relative;width:38px;padding:0;flex:none}
.bell i{
  position:absolute;top:-3px;right:-3px;min-width:17px;height:17px;padding:0 4px;
  border-radius:var(--r-pill);background:var(--accent);color:var(--accent-ink);
  font-size:10.5px;font-weight:800;font-style:normal;line-height:17px;text-align:center;
}
.bell[aria-expanded=true]{background:var(--surface-3)}


.main{min-width:0;padding-bottom:76px}

/* ── section head ───────────────────────────────────────────────── */
.sec{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;padding:20px 14px 14px}
.sec h1,.sec h2{font-size:clamp(21px,2.4vw,26px);line-height:1.15}
.sec p{font-size:13.5px;font-weight:600;color:var(--text-3);white-space:nowrap}
.sec b{color:var(--text-2);font-variant-numeric:tabular-nums}

/* ── entity header ──────────────────────────────────────────────
   A profile and a community are the same object to a reader: a name, a
   line about it, the numbers that say whether it is worth your time, and
   one action. Core, so both get it. */
.pro{display:flex;align-items:flex-start;gap:13px;padding:20px 14px 0}
/* flex-start is right when the bio gives this block two or three lines. With
   no bio it is one line of name against a 72px disc, and the name ends up
   pinned to the top with forty pixels of nothing under it.

   :has() rather than a server flag, so it is true whenever the bio is
   actually absent instead of whenever someone remembered to set an
   attribute. Same support baseline as .crow:has() and .fol:has(), already
   in use here. */
.pro:not(:has(.pro-bio)){align-items:center}
.pro-av{
  width:60px;height:60px;border-radius:50%;flex:none;
  display:grid;place-items:center;
  font-size:25px;font-weight:800;text-transform:uppercase;
}
/* ── presence ───────────────────────────────────────────────────
   A dot on the face when someone is here now, and a bucket in the stat
   line when they are not.

   Buckets, not clock times. "Active today" carries the social signal —
   is this person still around, is it worth following them — while "Last
   online 23:40" carries a second one nobody asked to publish: when this
   man is awake, alone, and at home. On this site that is not a hypothetical
   cost, and the coarse version answers the question people actually have. */
.pro[data-live=now] .pro-av{position:relative}
.pro[data-live=now] .pro-av::after{
  content:"";position:absolute;right:1px;bottom:1px;
  width:14px;height:14px;border-radius:50%;
  background:var(--live);box-shadow:0 0 0 3px var(--bg);
}
/* Not on your own profile. Presence is information for other people — you
   already know whether you are here, and the corner is wanted for something
   you can act on. This is also what stops the dot and the pencil fighting
   for the same eight pixels. */
.pro[data-me][data-live=now] .pro-av::after{content:none}
/* The word, and no second dot in front of it. The dot on the avatar and
   this line are a glyph and its label, not two signals — but a dot, then
   another dot, then the word is three tellings of one fact.

   The label stays rather than the dot doing it alone: it is the only
   carrier when someone is offline, and it keeps the state from being
   conveyed by colour by itself, which a green disc on its own would be. */
.stats .live{color:var(--text-2);font-weight:700}
.pro[data-live=now] ~ .stats .live{color:var(--live)}

.pro-b{min-width:0;flex:1}
.pro-b h1{font-size:clamp(21px,2.4vw,26px);line-height:1.15}
.pro-b h1 .tick{
  display:inline-grid;place-items:center;vertical-align:2px;margin-left:7px;
  width:17px;height:17px;border-radius:50%;background:var(--accent);
}
.pro-b h1 .tick svg{width:10px;height:10px;stroke:var(--accent-ink);stroke-width:2.8;fill:none}
.pro-bio{font-size:14px;font-weight:500;color:var(--text-2);line-height:1.5;padding-top:5px;max-width:62ch}
.pro-bio a{color:var(--accent)}
/* On your own profile an empty bio is a gap you can close, so it occupies
   the bio's slot as an invitation rather than collapsing. It keeps the
   class, which means the header stays top-aligned and the fix above does
   not fight it. On somebody else's, an empty bio is just a fact and the
   header centres instead. */
/* ── the unfilled avatar ────────────────────────────────────────
   Same idea as the bio invitation below: on your own profile a thing you
   have not filled in becomes the way to fill it. The whole disc is the
   target, not the badge — 72px beats 26px, and on a phone the badge alone
   would be under the touch minimum. */
.av-add{position:relative;overflow:visible}
.av-pen{
  position:absolute;right:-3px;bottom:-3px;
  width:26px;height:26px;padding:6px;border-radius:50%;
  background:var(--surface-3);box-shadow:0 0 0 3px var(--bg);
  stroke:var(--text-2);fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;
  transition:background .14s var(--ease),stroke .14s var(--ease);
}
.av-add:hover .av-pen{background:var(--accent);stroke:var(--accent-ink)}
/* the letter keeps its own colour; only the badge reacts */
.av-add:hover{color:inherit}

/* .pro-bio a is (0,1,1) and would otherwise win, painting this accent —
   two pink things in one header, competing with Follow. A nudge, not a
   primary action. */
.pro-bio a.bio-add{display:inline-block;color:var(--text-3);font-weight:600}
.pro-bio a.bio-add:hover{color:var(--text)}
.pro-acts{display:flex;gap:8px;flex:none}
/* Follow, Join and Subscribe are the same shape of control on three kinds of
   entity, so they share one state machine: aria-pressed, and nothing else.
   Three separate handlers were each toggling .btn-accent by hand, which is a
   class and an attribute that can disagree — and did, on the collection page.
   Off is loud because it is an invitation; on is quiet because it is a fact. */
.pro-acts .btn[aria-pressed=false]{background:var(--accent);color:var(--accent-ink)}
.pro-acts .btn[aria-pressed=false]:hover{background:var(--accent-hi)}
.pro-acts .btn[aria-pressed=true]{background:var(--surface-2);color:var(--text-2)}
.pro-acts .btn[aria-pressed=true]:hover{background:var(--surface-3);color:var(--text)}

/* The numbers a stranger uses to decide whether this person is worth
   following. On the live site none of them are on the profile at all. */
.stats{
  display:flex;flex-wrap:wrap;gap:5px 20px;padding:13px 14px 0;
  font-size:13.5px;font-weight:600;color:var(--text-3);
}
.stats b{color:var(--text);font-weight:800;font-variant-numeric:tabular-nums}
.stats a:hover b{color:var(--accent)}
.stats .quiet{color:var(--text-3)}


.pempty{padding:34px 14px 40px;max-width:52ch}
.pempty h2{font-size:19px;line-height:1.25;padding-bottom:8px}
.pempty p{font-size:14.5px;font-weight:500;line-height:1.55;color:var(--text-2);padding-bottom:16px}


/* the sort/filter row under a section head. Lives here, not in a page
   sheet — four pages use it, and a page that forgot to load its copy
   dropped the whole row out of the gutter. */
.ctrl{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:0 14px 16px}
.ctrl-gap{flex:1}

/* ── the grid of cards ──────────────────────────────────────────── */
.grid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:14px 10px;padding:0 14px;
}
.card{display:flex;flex-direction:column;min-width:0;border-radius:var(--r-md)}
.thumb{
  position:relative;aspect-ratio:16/9;border-radius:var(--r-md);
  background:var(--surface-2);overflow:hidden;flex:none;
}
.t1{background:var(--t1)}.t2{background:var(--t2)}.t3{background:var(--t3)}.t4{background:var(--t4)}
.t5{background:var(--t5)}.t6{background:var(--t6)}.t7{background:var(--t7)}.t8{background:var(--t8)}
.dur{
  position:absolute;right:6px;bottom:6px;
  background:rgba(0,0,0,.78);color:#fff;border-radius:6px;
  padding:2px 6px;font-size:11.5px;font-weight:700;letter-spacing:.01em;
  font-variant-numeric:tabular-nums;
}
/* caution, stated on the thumbnail before anyone clicks */
.ads{
  position:absolute;left:6px;top:6px;
  background:var(--warn);color:var(--warn-ink);border-radius:6px;
  padding:2px 6px;font-size:11px;font-weight:800;letter-spacing:.02em;
}
/* the play affordance, drawn in CSS so it costs nothing per card.
   Appears on hover; hides nothing at rest. */
.thumb::after,.thumb::before{content:"";position:absolute;opacity:0;transition:opacity .16s var(--ease)}
.thumb::after{
  left:50%;top:50%;width:46px;height:46px;margin:-23px 0 0 -23px;
  border-radius:50%;background:var(--accent);
}
.thumb::before{
  z-index:1;left:50%;top:50%;margin:-8px 0 0 -5px;
  border-left:14px solid var(--accent-ink);
  border-top:8px solid transparent;border-bottom:8px solid transparent;
}
@media (hover:hover){
  .card:hover .thumb::after,.card:hover .thumb::before{opacity:1}
  .card:hover .card-title{color:var(--accent)}
}
.card:focus-visible .thumb::after,.card:focus-visible .thumb::before{opacity:1}
/* touch has no hover, so the press itself has to answer */
.card:active .card-title{color:var(--accent)}
.card:active .thumb{opacity:.72}

.card-body{padding:9px 2px 0;min-width:0}
.card-title{
  font-size:14.5px;font-weight:700;line-height:1.3;letter-spacing:-.012em;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.card-sub{
  display:flex;align-items:center;gap:6px;
  padding-top:4px;font-size:12.5px;font-weight:600;color:var(--text-2);
}
/* the host gives way, never the like count — and the separator can no
   longer be orphaned at the end of a line */
.card-sub .host{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-sub .dot,.card-sub .k{flex:none;white-space:nowrap}
.card-by{
  display:flex;align-items:center;gap:6px;padding-top:5px;
  font-size:12.5px;font-weight:500;color:var(--text-3);min-width:0;
}
/* The letter is centred by grid, so any ancestor rule that resets this to
   display:block drops the glyph out of the disc. Descendant selectors that
   style spans in a container holding an avatar must exclude it —
   .card .who span:not(.av), and so on. */
.av{
  width:18px;height:18px;border-radius:50%;flex:none;
  background:var(--surface-3);color:var(--text-2);
  display:grid;place-items:center;font-size:9.5px;font-weight:800;text-transform:uppercase;
}
.dot{color:var(--text-3)}

/* avatars borrow the thumbnail palette. Core, not a page sheet — the search
   and notification pages use them and loaded neither copy. */
/* avatars borrow the thumbnail palette */
.a1{background:var(--t1);color:#fff}.a2{background:var(--t2);color:#fff}
.a3{background:var(--t3);color:#fff}.a4{background:var(--t4);color:#fff}
.a5{background:var(--t5);color:#fff}.a6{background:var(--t6);color:#fff}
.a7{background:var(--t7);color:#fff}.a8{background:var(--t8);color:#fff}

.empty{padding:44px 14px;color:var(--text-2);font-size:16px;font-weight:600}
.empty a{color:var(--accent)}

/* ── mobile tab bar — the app shell on small screens ────────────── */
.tabs{
  position:fixed;left:0;right:0;bottom:0;z-index:30;
  display:flex;background:var(--bg);
  border-top:1px solid var(--line);padding-bottom:env(safe-area-inset-bottom);
}
.tabs a{
  flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:9px 0 8px;font-size:10.5px;font-weight:700;color:var(--text-3);
}
.tabs a.on{color:var(--accent)}
.tabs .icon{width:21px;height:21px}

/* Typing in the top bar filters the rows the browser already holds — fast,
   and useless past row 24. This is the way out, and it only exists while a
   query is live. */
.allq{
  display:flex;align-items:center;gap:9px;
  margin:0 14px 14px;padding:13px 15px;
  background:var(--surface);border-radius:var(--r-md);
  font-size:14px;font-weight:700;color:var(--text);
}
.allq b{font-variant-numeric:tabular-nums}
.allq span{color:var(--accent)}
.allq .icon{margin-left:auto;color:var(--text-3)}
.allq:active{background:var(--surface-2)}

/* ── phone: two navigations, no overlap ─────────────────────────
   The bottom bar owns Home / Collections / Stash / Communities / Following.
   The top rail owns everything else. Repeating five destinations in both
   was what made the rail long enough to need scrolling in the first place. */
@media (max-width:699px){
  /* .pro-acts is flex:none, so on a 390px screen the buttons took what they
     wanted and the bio got what was left — which was one word per line. Below
     tablet they take their own row under the name instead. Applies to
     profiles and communities alike; both were squeezed, the community worse
     because its handle line sits above the bio. */
  .pro{flex-wrap:wrap}
  .pro-acts{width:100%;padding-top:13px}
  .side-nav a.dup{display:none}
  /* "Stash a link" is the middle tab in the bottom bar. Repeating it here
     was costing the search field a third of its width, on the one control
     that most arrivals came to use. */
  .top-acts .tdup{display:none}
  /* mid-query the categories are noise, and on a 390px screen with the
     keyboard up they cost a fifth of what's left. Results move up under
     the field the moment anything is typed. */
  body.q-on .side{display:none}
  /* the keyboard is over the tab bar; give the space to the results */
  body.kb .tabs{display:none}
  body.kb .main{padding-bottom:20px}
  .top{gap:6px}
  .top-acts{gap:2px}
  /* sections and categories are different questions — the rail says so
     with a rule rather than running them into one undifferentiated list */
  .side-lab{
    display:block;flex:none;align-self:center;
    width:1px;height:22px;padding:0;margin:0 3px;
    background:var(--line-2);font-size:0;
  }
  .top{padding:8px 14px}
  .main{padding-bottom:calc(72px + env(safe-area-inset-bottom))}
}

/* ── tablet: the rail stands up ─────────────────────────────────
   At 834px a horizontal scroller wastes the one thing a tablet has, which
   is height. Collapsed to a column of icons every section is reachable
   without a swipe, and the grid keeps all three columns. */
@media (min-width:700px) and (max-width:999px){
  .app{grid-template-columns:92px minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}
  .side{grid-row:1/3}
  .top,.main{grid-column:2}
  .side{
    flex-direction:column;gap:2px;align-items:stretch;
    position:sticky;top:0;height:100vh;overflow-x:visible;overflow-y:auto;
    padding:10px 8px calc(12px + env(safe-area-inset-bottom));
    background:var(--surface);border-right:1px solid var(--line);
    -webkit-mask-image:none;mask-image:none;
  }
  .side .brand{display:flex;justify-content:center;padding:6px 0 12px;font-size:0;gap:0}
  .side .mark{width:26px;height:26px}
  .side-nav,.side-cats{display:flex;flex-direction:column;gap:2px}
  .side-nav .icon{display:block;width:20px;height:20px}
  .side-nav a{
    flex-direction:column;gap:4px;height:auto;padding:9px 2px;
    border-radius:var(--r-md);background:transparent;text-align:center;
    font-size:10.5px;font-weight:700;line-height:1.2;white-space:normal;
  }
  .side-nav a.on{background:var(--surface-2);color:var(--text)}
  .side-lab{display:block;font-size:10px;font-weight:800;letter-spacing:.05em;
    color:var(--text-3);padding:14px 0 6px;text-align:center}
  .side-cats a{
    height:32px;justify-content:center;padding:0 4px;border-radius:var(--r-sm);
    background:transparent;font-size:12px;font-weight:600;color:var(--text-3);
  }
  .side-cats a.on{color:var(--accent)}
  .tabs{display:none}
  .main{padding-bottom:30px}
  .top{padding:11px 18px}
  .sec{padding:22px 18px 14px}
  .ctrl{padding:0 18px 16px}
  .grid{padding:0 18px;gap:18px 12px}
  .allq{margin:0 18px 14px}
}

/* ── desktop: the persistent sidebar ────────────────────────────── */
@media (min-width:1000px){
  .app{grid-template-columns:var(--side-w) minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}
  .side{grid-row:1/3}
  .top,.main{grid-column:2}
  /* the name only where there is room for it — below this the search field
     is already fighting the bell and Stash a link for the same pixels */
  .acct-name{display:block}
  .acct-chev{display:block}
  .acct-btn{padding:0 4px 0 6px}
  /* the avatar goes 60 → 72 here, so the dot goes with it */
  .pro[data-live=now] .pro-av::after{width:16px;height:16px;right:2px;bottom:2px}
  .side{
    display:flex;flex-direction:column;gap:4px;
    position:sticky;top:0;height:100vh;padding:16px 10px;
    background:var(--surface);border-right:1px solid var(--line);overflow-y:auto;
  }
  .side .brand{display:flex;padding:6px 10px 14px;font-size:21px}
  .side-lab,.side-foot{display:block}
  .side-nav .icon{display:block}
  .side-nav{display:flex;flex-direction:column;gap:2px}
  .side-nav a{
    display:flex;align-items:center;gap:11px;height:40px;padding:0 12px;
    border-radius:var(--r-sm);font-size:14.5px;font-weight:700;color:var(--text-2);
    transition:background .14s var(--ease),color .14s var(--ease);
  }
  .side-nav a:hover{background:var(--surface-2);color:var(--text)}
  .side-nav a.on{background:var(--surface-2);color:var(--text)}
  .side-lab{font-size:11.5px;font-weight:800;letter-spacing:.06em;color:var(--text-3);padding:18px 12px 7px}
  .side-cats{display:flex;flex-direction:column;gap:1px}
  .side-cats a{
    height:34px;display:flex;align-items:center;padding:0 12px;border-radius:var(--r-sm);
    background:transparent;font-size:14px;font-weight:600;color:var(--text-3);
  }
  .side-cats a:hover{background:var(--surface-2);color:var(--text)}
  .side-cats a.on{background:transparent;color:var(--accent)}
  .side-nav a{background:transparent;border-radius:var(--r-sm);height:40px;padding:0 12px;font-size:14.5px;font-weight:700}
  .side-nav a.on{background:var(--surface-2);color:var(--text)}
  .side-foot{margin-top:auto;padding:16px 12px 4px;font-size:12px;font-weight:500;color:var(--text-3);line-height:1.55}
  .side-foot b{color:var(--text-2);font-weight:700;font-variant-numeric:tabular-nums}
  .tabs{display:none}
  .main{padding-bottom:30px}
  .top{padding:12px 22px}
  .sec{padding:28px 22px 16px}
  .ctrl{padding:0 22px 18px}
  .grid{padding:0 22px;gap:20px 14px}
  .allq{margin:0 22px 16px}
}
@media (min-width:640px){.grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:1000px){.grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:1240px){.grid{grid-template-columns:repeat(4,minmax(0,1fr))}}
@media (min-width:1560px){.grid{grid-template-columns:repeat(5,minmax(0,1fr))}}
@media (min-width:1860px){.grid{grid-template-columns:repeat(6,minmax(0,1fr))}}

/* ── coarse pointers ────────────────────────────────────────────
   A finger is about 9mm. Every target grows to clear 44px; the type does
   not, so density is unchanged — only the space around it. */
@media (pointer:coarse){
  .btn{height:44px;padding:0 18px}
  .btn-lg{height:50px}
  .btn-sm{height:38px;padding:0 15px}
  .pill{height:40px;padding:0 16px}
  .side-nav a,.side-cats a{height:40px}
  .search input{height:44px}
  .tabs a{padding:10px 0 9px;font-size:11px}
  .tabs .icon{width:23px;height:23px}
  .card-title{font-size:15px}
  /* rows that are links get the same floor */
  .tg,.pr,.nrow,.coll,.fol,.prow{min-height:48px}
}

@media (prefers-reduced-motion:reduce){
  *{transition:none !important;animation:none !important}
}
