/* Trending rail. Fixed to the right of the feed, and only when there is real
   room for it: below this width the feed is the whole point of the screen. */
/* The feed and the rail are one centred row, not a column with something
   pinned to the far right: pinning left a canyon between them on wide
   screens. Sticky rather than fixed, so the rail starts level with the first
   card and stays put on its own. */
.cols{padding-top:70px}
@media (max-width:900px){ .cols{padding-top:64px} }
/* The feed is centred on the window, under the wordmark, and nothing beside it
   is allowed to move it. Between the bottom of the nav breakpoint and the width
   where a 620px column clears the nav, the column narrows instead of shifting. */
.cols main.wrap{margin:0 auto;width:100%;max-width:620px}
@media (min-width:901px) and (max-width:1119px){
  .cols main.wrap{max-width:calc(100vw - 2 * 252px)}
}
#nav-trends{display:none}
@media (max-width:900px){ #nav-trends{display:flex} }
/* Out of the flow entirely, pinned to the right of the centred column, so it
   can never push the feed off the wordmark. It simply stops appearing when
   there is no longer room beside the column. */
.rrail{display:none}
@media (min-width:1240px){
  body.has-rail .rrail{display:block;position:fixed;top:84px;left:calc(50% + 332px);
    width:min(var(--railmax), calc(50vw - 356px));
    max-height:calc(100vh - 104px);overflow-y:auto;scrollbar-width:none;z-index:5}
  .rrail::-webkit-scrollbar{display:none}
}
.tr-card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:14px 4px 6px}
.tr-card h2{font-size:1.02rem;font-weight:800;margin:0 0 4px;padding:0 12px}
.tr-card .rsub{font-size:.78rem;color:var(--text2);padding:0 12px 8px;margin:0}
/* The rail's own tabs and the panes behind them. */
/* Three tabs, one row, no leftover: each takes a third of the rail. */
.rtabs{padding:0 12px;margin:2px 0 10px;gap:0;overflow:visible}
.rtabs .psort-chip{padding:10px 4px;font-size:.88rem}
/* A group has no picture, so the rank stands on its own in the same square. */
.rt-flat{background:var(--accent-soft);display:flex;align-items:center;justify-content:center}
.rt-flat .rt-rank{position:static;background:none;color:var(--accent);font-size:1.05rem;backdrop-filter:none}
.rgroup-grid{padding:0 12px 8px}
/* A comment in the People pane: who said it, what it answers, and what it drew. */
.slow-note{font-size:.85rem;color:var(--text2);text-align:center;padding:4px 0 10px}
.saidrow{cursor:pointer}
.said-to{display:block;font-size:.74rem;color:var(--text2);margin-bottom:2px}
.saidrow .rt-blurb{-webkit-line-clamp:3}
/* An account row is a trending row with an avatar where the picture goes. */
/* A group or an account, read inside the app: the same column as the feed,
   with the way back at the top of it. */
/* The bio spans the whole card. It used to sit in the row with the avatar and
   the Follow button, which on a phone left it about half the width and made
   a short bio four lines tall. */
.pfhead{padding:4px 2px 18px}
.pfhead-top{display:flex;gap:14px;align-items:center}
.pfhead-id{flex:1;min-width:0}
.pfhead-id h1{font-size:1.3rem;font-weight:800;margin:0}
.pfhead-bio{font-size:.94rem;color:var(--text2);line-height:1.45;margin:12px 0 0;overflow-wrap:anywhere}
.pfhead-counts{font-size:.86rem;color:var(--text2);margin-top:8px}
.av-lg{width:76px;height:76px;border-radius:20px;font-size:1.7rem}
/* The double-tap burst: a heart that pops and six sparks thrown out around it.
   Absolutely placed at the tap, and removed when it finishes. */
.lburst{position:absolute;width:0;height:0;pointer-events:none;z-index:6}
.lburst img{position:absolute;width:78px;height:78px;left:-39px;top:-39px;
  animation:lburst-pop .72s cubic-bezier(.2,.9,.3,1) forwards;filter:drop-shadow(0 4px 14px rgba(0,0,0,.35))}
@keyframes lburst-pop{
  0%{transform:scale(.2) rotate(-12deg);opacity:0}
  22%{transform:scale(1.18) rotate(4deg);opacity:1}
  46%{transform:scale(.98) rotate(0)}
  100%{transform:scale(1.42);opacity:0}
}
.lburst i{position:absolute;left:-3px;top:-3px;width:6px;height:6px;border-radius:50%;
  background:var(--accent);transform:rotate(var(--a)) translateY(0);
  animation:lburst-spark .68s ease-out forwards}
@keyframes lburst-spark{
  0%{opacity:0;transform:rotate(var(--a)) translateY(-6px) scale(.6)}
  25%{opacity:1}
  100%{opacity:0;transform:rotate(var(--a)) translateY(-54px) scale(1)}
}
@media (prefers-reduced-motion: reduce){
  .lburst img{animation-duration:.01s}
  .lburst i{display:none}
}
/* A comment's like sits under its text, small, and only shows a number when
   there is one. */
.rc-like{margin-top:4px;padding-left:0}
.rperson{align-items:center;cursor:pointer}
.rperson .rt-txt{flex:1;min-width:0}
/* The follow badge sits proud of the avatar's bottom corner, so the square it
   is in cannot clip. Written against .rperson rather than as its own class,
   because .rt-shot sets overflow:hidden further down the file and a class on
   its own would lose to it. */
.rperson .rt-shot{background:none;overflow:visible}
.av-rail{width:92px;height:92px;border-radius:12px;font-size:1.8rem}
.rperson .follow-btn{flex:none;align-self:center}
.rtrend{display:flex;align-items:flex-start;gap:12px;width:100%;text-align:left;background:none;border:none;color:inherit;cursor:pointer;padding:10px 12px;border-radius:13px;text-decoration:none}
.rtrend:hover{background:var(--accent-soft)}
/* The picture carries the rank, so the number reads at a glance instead of
   hiding in a line of small grey text. */
.rt-shot{position:relative;width:92px;height:92px;flex-shrink:0;border-radius:12px;overflow:hidden;background:var(--line)}
.rt-thumb{width:100%;height:100%;object-fit:cover;display:block}
.rt-rank{position:absolute;top:5px;left:5px;min-width:23px;height:23px;padding:0 6px;border-radius:8px;background:rgba(0,0,0,.66);color:#fff;font-size:.76rem;font-weight:800;display:flex;align-items:center;justify-content:center;line-height:1;backdrop-filter:blur(2px)}
.rt-txt{flex:1;min-width:0;padding-top:2px}
.rtrend .rt-term{display:block;font-weight:700;font-size:1rem;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rtrend .rt-blurb{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-size:.82rem;line-height:1.35;color:var(--text2);margin-top:3px}
.rtrend .rt-meta{display:block;font-size:.76rem;color:var(--text2);margin-top:4px}
/* The trending card the mobile feed drops in every ten to fifteen klips. A
   rail of its own that scrolls sideways, one klip per tile. */
.tspot{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:14px 0 12px;margin-bottom:12px}
.tspot h3{font-size:1.02rem;font-weight:800;margin:0 0 2px;padding:0 14px}
.tspot .ts-sub{font-size:.78rem;color:var(--text2);padding:0 14px 11px;margin:0}
.ts-track{display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;padding:0 14px 2px;-webkit-overflow-scrolling:touch;scrollbar-width:none;overscroll-behavior-x:contain}
.ts-track::-webkit-scrollbar{display:none}
.ts-tile{flex:0 0 158px;scroll-snap-align:start;background:var(--bg);border:1px solid var(--line);border-radius:14px;overflow:hidden;text-align:left;padding:0;cursor:pointer;color:inherit;display:flex;flex-direction:column}
.ts-tile img,.ts-noimg{width:100%;aspect-ratio:1/1;object-fit:cover;display:block;background:var(--line)}
.ts-body{padding:9px 10px 4px;font-size:.82rem;line-height:1.35;flex:1;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.ts-who{padding:0 10px;font-size:.74rem;color:var(--text2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ts-stats{display:flex;gap:9px;padding:7px 10px 10px;font-size:.72rem;color:var(--text2)}
.ts-stats span{display:inline-flex;align-items:center;gap:3px}
.ts-stats svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
@media (min-width:1200px){ .tspot{display:none} }
.tr-card .rempty{padding:2px 12px 10px;font-size:.84rem;color:var(--text2);line-height:1.45}
.tback{display:flex;align-items:center;gap:8px;background:none;border:none;color:var(--text2);cursor:pointer;font-size:.9rem;font-weight:600;padding:0;margin-bottom:12px}
.tback svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.thead{margin-bottom:14px}
.thead h1{font-size:1.5rem;font-weight:800;margin:0}
.thead-sub h2{font-size:1.05rem;font-weight:800;margin:0}
.thead p{margin:3px 0 0;font-size:.86rem;color:var(--text2)}
#app main.wrap{padding-top:0}

/* Left sidebar (desktop) becomes a bottom bar on narrow screens. */
.sidebar{position:fixed;top:0;left:0;bottom:0;width:var(--sidebar);background:var(--card);border-right:1px solid var(--line);display:flex;flex-direction:column;padding:74px 12px 16px;z-index:30}
.snav{display:flex;flex-direction:column;gap:4px;flex:1}
.snav button{display:flex;align-items:center;gap:12px;padding:11px 13px;border-radius:12px;font-size:1rem;font-weight:600;color:var(--text2);text-align:left;width:100%}
.snav button:hover{background:var(--accent-soft);color:var(--text)}
.snav button.active{background:var(--accent);color:#fff}
.snav .sicon{width:20px;height:20px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}
.snav .sicon svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.sfoot{border-top:1px solid var(--line);padding-top:12px;margin-top:12px}
/* The nav no longer reserves a strip of the page: the feed is centred on the
   window under the wordmark, and the nav floats over the margin beside it.
   The narrow band where a 620px column would reach the nav is handled by
   narrowing the column, not by shoving it sideways. */
body.signed-in{padding-left:0}
/* The bar floats clear of the screen edge as a pill, and the tab you are on is
   a raised sphere sitting proud of it rather than a tint behind a label.
   Dropping the labels is what finally makes five fit: the old bar could only
   hold four, which is why Groups used to be hidden here. Settings leaves the
   bar because it is already in the profile menu at the top. */
@media (max-width:900px){
  body.signed-in{padding-left:0;padding-bottom:calc(64px + env(safe-area-inset-bottom))}
  /* Straight across the bottom, edge to edge, with only a rule along its top:
     the same shape and spacing as Kreatr's mobile bar. The home indicator is
     cleared by padding inside the bar, which is also why the icons read as
     centred now: that clearance is the strip the indicator sits in rather
     than empty space under a floating pill. */
  .sidebar{top:auto;left:0;right:0;bottom:0;
    width:auto;flex-direction:row;border:none;border-top:1px solid var(--line);border-radius:0;
    padding:6px 4px calc(6px + env(safe-area-inset-bottom));
    align-items:center;gap:0;overflow:visible;box-shadow:none}
  .sidebar .sfoot-label,.sidebar .sfoot{display:none}
  .snav{flex-direction:row;gap:0;position:relative;width:100%;padding:0}
  /* 48px tall and centred, so a 20px icon still has its middle 24px down and
     the sphere's arithmetic below holds unchanged. */
  .snav button{flex:1 1 0;min-width:0;flex-direction:column;gap:0;padding:6px 2px;
    min-height:48px;justify-content:center;align-items:center;text-align:center;
    border-radius:10px;position:relative;z-index:2}
  /* Icons only. A label under a 44px sphere has nowhere to go. */
  .snav button > span:not(.sicon){display:none}
  .snav button:hover{background:transparent;color:var(--text2)}
  .snav button.active{background:transparent;color:#fff}
  .snav button.active .sicon{transform:translateY(calc(-1px - var(--nav-lift)))}
  .snav .sicon{transition:transform .36s cubic-bezier(.34,1.26,.52,1)}
  @media (prefers-reduced-motion: reduce){ .snav .sicon{transition:none} }
  .snav .sicon svg{stroke-width:2}
  #nav-groups{display:flex}
  /* Settings lives in the profile menu, so it does not spend a slot down here. */
  #nav-settings{display:none}
}
.hidden{display:none !important}
.vhide{visibility:hidden}
/* An open modal owns the scroll: the page behind it stays put. */
body:has(.modal-back:not(.hidden)),body:has(.gif-back:not(.hidden)){overflow:hidden}
/* The Desk is a desktop tool. */
@media (max-width:900px){ #um-desk{display:none !important} }
/* Admin shortcut on the left of the bar, every page, admin sessions only. */
.h-admin{display:inline-flex;align-items:center;justify-content:center;gap:6px;text-align:center;font-size:.84rem;font-weight:700;padding:7px 13px;border-radius:999px;border:1.5px solid var(--line);color:var(--text2);pointer-events:auto}
.h-admin:hover{border-color:var(--accent);color:var(--accent)}
.h-admin svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
@media (max-width:900px){ .h-admin{display:none} }

/* Topbar: pinned to the top right of the window, same as Kreatr. */
/* The wordmark is the centre line of the whole product: the feed sits under
   it and everything else arranges around it. So it is centred on the window,
   not on whatever space is left over beside the nav. */
header{position:fixed;top:0;left:0;right:0;z-index:90;height:60px;display:flex;align-items:center;pointer-events:none;
  background:color-mix(in srgb, var(--bg) 88%, transparent);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.hbar{position:relative;display:flex;align-items:center;width:100%;max-width:none;margin:0;padding:0 20px;height:60px}
.hbar .logo{position:absolute;left:50%;transform:translateX(-50%);pointer-events:auto}
/* The wordmark used to hide on desktop because the nav carried its own. There
   is one now, here, and it shows at every width. */
@media (max-width:900px){
  header{left:16px;right:16px;top:10px;height:48px;border-radius:24px;padding:0 6px;border-bottom:none}
  .hbar{height:48px;padding:0 8px}
}
.logo{font-weight:800;font-size:1.45rem;letter-spacing:-0.03em;color:var(--accent)}
.logo-img{height:26px;display:block}
.logo-img-lg{height:44px;display:inline-block}
.hactions{display:flex;align-items:center;gap:10px;position:relative;margin-left:auto;pointer-events:auto}
.hprofile{display:flex;align-items:center;gap:10px;border-radius:11px;padding:0}
.icon-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;color:var(--text2)}
.icon-btn:hover{background:var(--accent-soft);color:var(--text)}
.icon-btn svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.user-menu{position:absolute;top:44px;right:0;width:250px;background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:0 14px 44px rgba(0,0,0,.24);padding:8px;z-index:40}
.um-head{display:flex;align-items:center;gap:11px;padding:10px 10px 12px}
.um-id{min-width:0}
.um-name{font-weight:700;font-size:.95rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.um-handle{color:var(--text2);font-size:.85rem}
.um-item{display:flex;align-items:center;gap:11px;width:100%;text-align:left;padding:10px;border-radius:11px;font-size:.94rem;font-weight:500;color:var(--text)}
.um-item:hover{background:var(--accent-soft)}
.um-item svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;color:var(--text2)}

/* Avatars: rounded squares, never circles */
.av{display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;flex-shrink:0;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;-webkit-user-drag:none;object-fit:cover}
.av-sm{width:42px;height:42px;border-radius:13px;font-size:1rem}
.av-xs{width:34px;height:34px;border-radius:11px;font-size:.85rem}

/* Landing: the pill is the one piece of it the app still uses, in the
   compose prompt. The rest went out to landing.css. */
.usp{display:inline-block;background:var(--accent-soft);color:var(--accent);font-weight:600;font-size:.85rem;padding:7px 14px;border-radius:999px;margin-bottom:18px}
.btn{display:inline-block;font-weight:600;border-radius:12px;padding:13px 26px;font-size:1rem;transition:opacity .15s;border:1.5px solid transparent;line-height:1.2}
.btn:active{opacity:.8}
.btn-primary{background:var(--accent);color:#fff}
.btn-ghost{border-color:var(--line);color:var(--text)}
.landing-actions{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
.landing-links{margin-top:30px;font-size:.85rem;color:var(--text2)}
.landing-links a{color:var(--text2)}
.landing-links a:hover{color:var(--text)}
.landing-links span{margin:0 9px;opacity:.5}

/* Compose */
/* Which feed you are reading, between the composer and the klips. Two words,
   because there are two answers: the whole room, or the part of it you chose. */
/* Same bar as the profile: equal segments, a quiet label, a rule under the one
   you are on. Only the look changes; every scope still does what it did. */
.feedpick{display:flex;gap:0;border-bottom:1px solid var(--line);margin:14px 0 16px;width:100%;max-width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
.feedpick::-webkit-scrollbar{display:none}
.feedpick.hidden{display:none}
/* Icon over a small label. The icons alone would be ambiguous for Everything
   and Following, which have no anchor elsewhere in the app the way the reply
   and paperclip marks do, so the label stays. The active state is carried by
   the underline and the weight, not by the icon, which is what lets the two
   drawn marks and the three hosted ones look identical in both themes. */
.fp-opt{flex:1 1 0;min-width:0;border:none;border-bottom:2px solid transparent;border-radius:0;
  background:none;color:var(--text2);font-weight:500;font-size:.68rem;padding:8px 2px 7px;
  cursor:pointer;line-height:1.15;white-space:nowrap;text-align:center;
  display:flex;flex-direction:column;align-items:center;gap:5px;
  transition:color .15s,border-color .15s}
.fp-ico{width:21px;height:21px;flex:none}
/* The hosted icons are drawn at this exact weight and colour, so the two drawn
   here match them rather than sitting a shade off. */
.fp-ico img,.fp-ico svg{width:21px;height:21px;display:block}
.fp-ico svg{fill:none;stroke:#8b8f98;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.fp-lab{overflow:hidden;text-overflow:ellipsis;max-width:100%}
.fp-opt:focus-visible{outline:2px solid var(--accent);outline-offset:-3px;border-radius:8px}
.fp-opt:hover{color:var(--text)}
.fp-opt.on{color:var(--text);border-bottom-color:var(--text);font-weight:700}
/* align-content defaults to stretch on a wrapping flex container, so every
   leftover pixel of the sheet was being shared out between the rows: the
   attachments bar measured 103px tall around a 32px button. Lines take their
   own height now and the slack collects at the bottom. */
.compose{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);margin:14px 0;padding:14px 14px 10px;display:flex;flex-wrap:wrap;align-content:flex-start;gap:11px;position:relative}
/* Klip stack composer. The parts already written sit in a numbered row under
   the text, so which klip is which is never a guess, and the one being
   written carries its position in the top right the way a photo Stack does.
   A row rather than a left column, so the avatar owns the far left and the
   text field keeps the full width. */
.crail{display:flex;gap:6px;width:100%;overflow-x:auto;padding-top:6px;scrollbar-width:none}
.crail::-webkit-scrollbar{display:none}
.cspine{width:34px;height:42px;border-radius:10px;border:1.5px solid var(--line);background:var(--bg);color:var(--text2);font-weight:800;font-size:.82rem;display:flex;align-items:center;justify-content:center;text-align:center;flex:none;position:relative}
.cspine:hover{border-color:var(--accent);color:var(--accent)}
.cspine.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
.cspine .cs-dot{position:absolute;bottom:5px;left:50%;transform:translateX(-50%);width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.75}
.cpos{position:absolute;top:11px;right:13px;display:flex;align-items:center;gap:4px;z-index:2}
.cpos-n{background:var(--accent-soft);color:var(--accent);font-size:.73rem;font-weight:800;padding:3px 9px;border-radius:999px;text-align:center}
.cpos-x{color:var(--text2);font-size:1.15rem;line-height:1;padding:1px 6px;border-radius:8px}
.cpos-x:hover{color:var(--red);background:var(--line)}
/* Room for the counter, so the first line of text never runs under it. */
.compose.stacked #compose-text{padding-right:80px}
/* Group on the left, what you do with the Klip on the right. The suggestion
   lives in the flexible middle and truncates, so it can never push the Klip
   button off its edge. */
.compose-media{width:100%;display:flex;flex-wrap:wrap;align-items:center;gap:6px 7px;padding-top:4px}
/* The two actions travel together and hold the right edge. Where the row is
   wide enough they sit beside the group picker, which is the whole point; on
   a narrow phone all four controls want 335px of a 283px row, so rather than
   squeezing the suggestion down to "+..." the pair wraps as a unit and stays
   right-aligned. */
.compose-acts{display:flex;align-items:center;gap:7px;margin-left:auto;flex:none}

/* Mention autocomplete: the list of accounts that match what has been typed
   after an @, sitting under the box being typed into. */
.mlist{width:100%;margin-top:9px;border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--card)}
.mrow{display:flex;align-items:center;gap:10px;width:100%;padding:9px 12px;text-align:left;background:none;border:none;color:var(--text);cursor:pointer}
.mrow:hover,.mrow.on{background:var(--accent-soft)}
.m-id{min-width:0;display:flex;flex-direction:column}
.m-name{font-weight:700;font-size:.9rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.m-handle{color:var(--text2);font-size:.82rem}
/* A tagged profile reads as a link, not as brand accent. */
.clip-mention,.clip-group{color:var(--link);font-weight:600}
/* A quotation is set apart rather than shouted: italic, and a purple light
   enough to read as a lift off the body text. The dark theme takes a paler
   one, since the light theme's would sink into the background there. */
.clip-quote{font-style:italic;color:#8b5cf6}
@media (prefers-color-scheme: dark){ html:not([data-theme="light"]) .clip-quote{color:#c4b5fd} }
html[data-theme="dark"] .clip-quote{color:#c4b5fd}
.clip-mention:hover{text-decoration:underline}
.cadd{display:inline-flex;align-items:center;justify-content:center;gap:6px;text-align:center;border:1.5px solid var(--line);color:var(--text);font-weight:700;font-size:.84rem;padding:7px 13px;border-radius:999px;white-space:nowrap;flex:none}
.cadd:hover{border-color:var(--accent);color:var(--accent)}
.cadd svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2.4;stroke-linecap:round}
/* Full-width foot row: the group dropdown's left edge sits flush with the
   avatar's left edge. min-width:0 on the text column stops a long suggestion
   chip from stretching the card and the counter bar with it. */
/* The suggestion chip lives in the flexible middle and truncates; it can
   never push the Klip button, which keeps its place at the right edge. */
/* Four lines at rest, five at most: past that the top line scrolls away
   rather than the sheet growing without end. 1.05rem x 1.45 is 24.4px a line,
   so five lines plus the top padding is 130px. */
.compose textarea{flex:1;border:none;outline:none;background:transparent;resize:none;font-size:1.05rem;line-height:1.45;min-height:104px;max-height:130px;overflow-y:auto;padding-top:8px;
  scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
/* The platform default was a bright bar sitting under the Emoji button. Thin,
   in the card's own line colour, with no track behind it. */
.compose textarea::-webkit-scrollbar{width:8px}
.compose textarea::-webkit-scrollbar-track{background:transparent}
.compose textarea::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px;
  border:2px solid transparent;background-clip:content-box}
.compose textarea:hover::-webkit-scrollbar-thumb{background:var(--text2);background-clip:content-box}
.compose-right{flex:1 1 100%;display:flex;flex-direction:column;min-width:0}
.compose-foot{display:flex;align-items:center;gap:10px;margin-top:8px;width:100%;flex-wrap:nowrap}
.gpick{position:relative}
.gpick-btn{display:flex;align-items:center;gap:5px;background:transparent;border:1px solid var(--line);border-radius:999px;padding:6px 12px;font-size:.85rem;color:var(--text2);max-width:220px}
.gpick-btn:hover{border-color:var(--accent);color:var(--text)}
.gpick-btn.set{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);font-weight:600}
.gpick-btn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gpick-arrow{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;flex-shrink:0}
/* Opens downward: the compose box sits at the top of the page, so there is
   never room above it for the list. */
.gpick-pop{position:absolute;top:calc(100% + 8px);left:0;width:270px;max-width:78vw;background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:0 12px 40px rgba(0,0,0,.22);z-index:30;overflow:hidden}
.gpick-pop input{width:100%;border:none;border-bottom:1px solid var(--line);background:transparent;padding:11px 13px;outline:none;font-size:.92rem}
.gpick-list{max-height:min(230px, 42vh);overflow-y:auto;padding:5px}
.gpick-item{display:block;width:100%;text-align:left;padding:9px 11px;border-radius:9px;font-size:.92rem;color:var(--text)}
.gpick-item:hover,.gpick-item.active{background:var(--accent-soft)}
/* Sub-groups. A child is indented under its parent in the picker, and marked
   in the chip grid when it is followed through that parent rather than
   directly, so what you are actually following is never a guess. */
.gpick-item.gpick-sub{padding-left:26px}
.group-chip.sub{font-size:.84rem}
.group-chip.via{border-style:dashed;color:var(--accent);border-color:var(--accent)}
.gpick-item.chosen{color:var(--accent);font-weight:700}
.gpick-none{color:var(--text2);font-size:.86rem;padding:12px 11px;text-align:center}
/* Sized to the suggestion it holds, not to the leftover space: as a greedy
   flex item it was squeezed to "+..." beside the group picker and the two
   buttons. The Klip button's own margin-left:auto still holds the right edge. */
.gsuggest{display:inline-flex;align-items:center;font-size:.84rem;color:var(--text2);min-width:0;overflow:hidden;flex:0 1 auto}
/* Italic placeholder while typing, so the row never changes height and the
   selector never gets pushed around. */
.gsuggest .g-wait{font-style:italic;color:var(--text2);opacity:.75;white-space:nowrap}
.gsuggest button{background:var(--accent-soft);color:var(--accent);font-weight:700;border-radius:999px;padding:6px 12px;font-size:.84rem;white-space:nowrap;max-width:150px;overflow:hidden;text-overflow:ellipsis}
.gsuggest button:hover{background:var(--accent);color:#fff}
/* A chosen group reveals an X over its label on hover, click to clear. */
.gpick-btn.set:hover span{opacity:0}
.gpick-btn .g-x{display:none;position:absolute;inset:0;align-items:center;justify-content:center;font-size:1.05rem;font-weight:700}
.gpick-btn.set{position:relative}
.gpick-btn.set:hover .g-x{display:flex}
.gpick-btn.set:hover .gpick-arrow{opacity:0}
.clip-btn{margin-left:auto;background:var(--accent);color:#fff;font-weight:700;border-radius:999px;padding:8px 20px;font-size:.95rem;flex:none;white-space:nowrap}
.clip-btn:disabled{opacity:.45;cursor:default}
.counter-track{height:3px;border-radius:2px;background:var(--line);margin-top:10px;overflow:hidden}
.counter-bar{height:100%;width:0%;background:var(--accent);border-radius:2px;transition:width .15s ease, background-color .35s ease}
.counter-bar.warn{background:var(--yellow)}
.counter-bar.danger{background:var(--red)}
.char-note{font-size:.78rem;color:var(--text2);margin:6px 0 3px;text-align:right;min-height:1em;font-variant-numeric:tabular-nums}
/* The number tracks the bar: plain, then yellow, then red past the limit. */
.char-note.warn{color:var(--yellow)}
.char-note.danger{color:var(--red)}

/* Tabs */
.tabs{display:flex;background:var(--card);border:1px solid var(--line);border-radius:999px;padding:4px;margin-bottom:14px}
.tabs button{flex:1;padding:9px 0;border-radius:999px;font-weight:600;font-size:.95rem;color:var(--text2)}
.tabs button.active{background:var(--accent);color:#fff}

/* Clip cards */
.clip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:14px;margin-bottom:12px;position:relative}
.clip-reclip-note{display:flex;align-items:center;gap:11px;color:var(--text2);font-size:.82rem;font-weight:600;margin-bottom:9px}
/* The icon sits in the avatar's column and the label starts where the name
   starts, so the card has two left edges rather than three. 42px is .av-sm
   and 11px is .clip-head's gap: change those and change these. */
.clip-reclip-note .crn-i{width:42px;flex:none;display:flex;justify-content:flex-end}
.clip-reclip-note img{width:14px;height:14px}
.clip-head{display:flex;gap:11px}
.clip-meta{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0;padding-right:26px}
.clip-stackn{background:var(--accent-soft);color:var(--accent);font-size:.68rem;font-weight:800;letter-spacing:.02em;padding:2px 7px;border-radius:999px;text-align:center;flex:none}
.clip-name{font-weight:700;font-size:.98rem}
.badge-img{width:16px;height:16px;vertical-align:text-top}
.clip-handle,.clip-time{color:var(--text2);font-size:.9rem}
.pfl{cursor:pointer}
.pfl:hover .clip-name,a.pfl.clip-handle:hover{text-decoration:underline}
.badge-btn{background:none;border:none;padding:0 0 0 3px;cursor:pointer;line-height:0;vertical-align:text-top}
.badge-pop{position:fixed;z-index:70;background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:0 12px 40px rgba(0,0,0,.28);padding:16px;width:290px;max-width:calc(100vw - 24px)}
.badge-pop h3{font-size:.95rem;display:flex;align-items:center;gap:7px;margin-bottom:6px}
.badge-pop h3 img{width:16px;height:16px}
.badge-pop p{font-size:.86rem;color:var(--text2);line-height:1.5}
.badge-pop a.bp-cta{display:block;text-align:center;background:var(--accent);color:#fff;font-weight:600;border-radius:10px;padding:9px 0;margin-top:12px;font-size:.9rem}
.badge-pop .bp-dis{font-size:.75rem;color:var(--text2);margin-top:9px;line-height:1.45}
.badge-pop .bp-x{position:absolute;top:8px;right:10px;background:none;border:none;color:var(--text2);font-size:1.2rem;cursor:pointer;line-height:1}
/* One control per klip, top right. It replaced the report flag and the
   highlighter, so it is always visible rather than revealed on hover: it is
   now the only way to reach report, highlight and delete. */
.clip-menu-btn{position:absolute;top:8px;right:8px;padding:7px;opacity:.5;transition:opacity .15s;z-index:2;line-height:0}
.clip:hover .clip-menu-btn,.clip-menu-btn:focus,.clip-menu-btn[aria-expanded="true"]{opacity:1}
.clip-menu-btn svg{width:17px;height:17px;stroke:var(--text2);fill:var(--text2);stroke-width:0}
.clip-menu{position:absolute;top:38px;right:8px;z-index:20;background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:0 12px 40px rgba(0,0,0,.28);padding:6px;min-width:236px;text-align:left;max-width:288px}
.clip-menu button{display:flex;align-items:center;gap:10px;width:100%;text-align:left;background:none;border:none;color:var(--text);padding:11px 12px;border-radius:10px;font-size:.93rem;font-weight:600;cursor:pointer}
.clip-menu button:hover{background:var(--accent-soft)}
/* Greyed rather than hidden: the reason it cannot be used is worth showing. */
.clip-menu button[disabled]{opacity:.45;cursor:default}
.clip-menu button[disabled]:hover{background:none}
.clip-menu svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;color:var(--text2)}
.clip-menu .cm-note{display:block;font-size:.76rem;font-weight:500;color:var(--text2);padding:0 12px 9px;line-height:1.4}
.clip-menu .cm-del,.clip-menu .cm-del svg{color:var(--red)}
/* Highlighted klip: kreatr.link's klip ring, an animated conic gradient
   sweeping around the card for the 24 hours the highlight lives. Browsers
   without @property get the same ring, standing still. */
@property --hl-a{syntax:'<angle>';inherits:false;initial-value:0deg}
.clip.hl{border-color:transparent}
.clip.hl::before{content:'';position:absolute;inset:-3px;border-radius:calc(var(--radius) + 3px);background:conic-gradient(from var(--hl-a,0deg),#7c3aed,#a78bfa,#60a5fa,#7c3aed);z-index:-1;animation:hl-spin 2s linear infinite}
@keyframes hl-spin{to{--hl-a:360deg}}
@media (hover:none){
  .clip-menu-btn{opacity:.6;padding:9px}
  .clip-menu-btn svg{width:19px;height:19px}
}
.drop{display:flex;align-items:center;gap:13px;border:1.5px dashed var(--line);border-radius:14px;padding:13px;margin:16px 0 4px;cursor:pointer;position:relative;transition:border-color .15s,background .15s}
.drop:hover{border-color:var(--accent);background:var(--accent-soft)}
.drop.over{border-color:transparent;box-shadow:0 0 0 2px var(--accent);outline:2px dashed var(--accent);outline-offset:-2px;background:rgba(124,58,237,.07)}
.drop .av{width:56px;height:56px;border-radius:16px;font-size:1.2rem}
.drop-txt{display:flex;flex-direction:column;gap:2px;min-width:0}
.drop-txt strong{font-size:.94rem}
.drop-txt span{font-size:.82rem;color:var(--text2)}
.drop-clear{position:absolute;top:8px;right:10px;font-size:1.2rem;line-height:1;color:var(--text2);padding:2px 6px;border-radius:8px}
.drop-clear:hover{background:var(--line);color:var(--text)}
.drop.busy{opacity:.6;pointer-events:none}
.del-modal{max-width:340px}
.del-acts{display:flex;gap:10px;margin-top:20px}
/* Same scope as .alt-acts: the Highlight modal's primary button was picking
   up `.modal .btn-primary`'s margin and sitting lower than its Cancel. */
.modal .del-acts .btn{flex:1;text-align:center;margin:0;width:auto;padding:12px 0;line-height:1.2}
.btn-danger{background:var(--red);color:#fff}

/* Pending delete: the klip stays put while a ten second timer runs, so undo
   costs nothing and the row underneath does not jump until it is really gone. */
.clip.pending-del{position:relative}
.clip.pending-del > *:not(.pd){opacity:.25;pointer-events:none}
.pd{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:14px;background:color-mix(in srgb, var(--card) 88%, transparent);border-radius:var(--radius)}
.pd-ring{position:relative;width:44px;height:44px;flex-shrink:0}
.pd-ring svg{width:44px;height:44px;transform:rotate(-90deg)}
.pd-ring circle{fill:none;stroke-width:3}
.pd-ring .r-bg{stroke:var(--line)}
.pd-ring .r-fg{stroke:var(--red);stroke-linecap:round;stroke-dasharray:119.4;stroke-dashoffset:0;animation:pd-drain 10s linear forwards}
@keyframes pd-drain{to{stroke-dashoffset:119.4}}
.pd-num{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:.95rem;color:var(--text)}
.pd-txt{font-size:.92rem;color:var(--text2)}
.pd-undo{font-weight:700;color:var(--accent);padding:8px 14px;border-radius:10px;font-size:.95rem}
.pd-undo:hover{background:var(--accent-soft)}
.clip.collapsing{overflow:hidden;transition:height .3s ease,opacity .25s ease,margin-bottom .3s ease,padding .3s ease;opacity:0;padding-top:0;padding-bottom:0;margin-bottom:0}
.report-reasons{display:flex;flex-direction:column;gap:9px;margin:16px 0 4px}
.report-reasons label{display:block;width:100%;border:1.5px solid var(--line);border-radius:12px;padding:13px 15px;cursor:pointer;font-size:.97rem;font-weight:500;color:var(--text);margin:0;transition:border-color .15s,background .15s,box-shadow .15s,color .15s}
.report-reasons label:hover{border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
/* Outranks the generic .modal input rule, which would otherwise stretch each
   hidden radio to full width and leave it overlaying the page. */
.modal .report-reasons input{position:absolute;opacity:0;width:1px;height:1px;margin:0;padding:0;border:0;pointer-events:none;clip:rect(0 0 0 0);overflow:hidden}
.report-reasons label.sel{border-color:var(--accent);background:var(--accent);color:#fff;box-shadow:0 0 0 4px var(--accent-soft)}
.clip-body{margin-top:4px;font-size:1.02rem;line-height:1.45;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:anywhere}
.clip-link{color:var(--accent);word-break:break-all}
.clip-tag{display:inline-block;background:var(--accent-soft);color:var(--accent);font-size:.78rem;font-weight:600;padding:3px 10px;border-radius:999px;margin-top:9px}
.clip-main{flex:1;min-width:0}
.linkcard{display:block;border:1px solid var(--line);border-radius:12px;overflow:hidden;margin-top:10px}
.linkcard img{width:100%;max-height:280px;object-fit:cover;display:block}
.linkcard .lc-t{padding:9px 12px;font-size:.9rem;font-weight:600}
.linkcard .lc-d{padding:0 12px 10px;font-size:.82rem;color:var(--text2)}
.actions{display:flex;justify-content:space-between;max-width:340px;margin-top:11px}
.act{display:flex;align-items:center;gap:6px;color:var(--text2);font-size:.85rem;padding:5px;border-radius:8px;transition:background .12s}
/* A count of zero renders an empty span, and an empty flex child still takes
   its gap: the highlight came out 6px wider than the icon and read as
   off-centre. No number, no child, no gap. */
.act > span:empty{display:none}
.act:hover{background:var(--accent-soft)}
.act img{width:19px;height:19px;display:block}
.act.liked span{color:var(--accent);font-weight:600}
.act.reclipped span{color:var(--accent);font-weight:600}
.act.reserved{opacity:.35;pointer-events:none}
/* Greyed but still tappable: the tap is what explains why it is greyed. */
.act.off{opacity:.35}
.act.saved img{filter:none}
.act.saved{color:var(--accent)}

/* Empty states / suggestions */
.empty{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:22px;text-align:center;color:var(--text2);margin-bottom:12px}
.empty h3{color:var(--text);margin-bottom:6px;font-size:1.05rem}
/* Swipeable profile tile. The row itself slides; the action sits in a layer
   behind it, revealed by the movement. Two trip-wires: past REVEAL the action
   reads as a menu, past COMMIT releasing fires it. Right follows (additive),
   left unfollows (the costly one), so direction stays a language. */
.tile{position:relative;overflow:hidden;border-radius:var(--radius);margin-bottom:10px;touch-action:pan-y}
.tile-bg{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between;border-radius:var(--radius);opacity:0;transition:opacity .12s}
.tile.dragging .tile-bg,.tile.armed .tile-bg{opacity:1}
.tile.sw-right .tile-bg{background:var(--accent)}
.tile.sw-left .tile-bg{background:var(--red)}
.tile-act{display:flex;align-items:center;gap:7px;padding:0 20px;color:#fff;font-weight:700;font-size:.9rem;opacity:.55;transition:opacity .12s,transform .12s;white-space:nowrap}
.tile.armed .tile-act{opacity:1;transform:scale(1.06)}
.tile-act svg{width:17px;height:17px;stroke:#fff;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.tile:not(.sw-right) .act-follow,.tile:not(.sw-left) .act-unfollow{visibility:hidden}
.sugg{position:relative;z-index:1;display:flex;align-items:center;gap:11px;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px;text-align:left;cursor:pointer;will-change:transform}
.sugg.settling{transition:transform .26s cubic-bezier(.22,1,.36,1)}
/* Studio */
.st-tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:12px}
@media (min-width:900px){.st-tiles{grid-template-columns:repeat(6,1fr)}}
.st-tile{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:14px 8px;text-align:center}
.st-n{font-weight:800;font-size:1.2rem}
.st-l{color:var(--text2);font-size:.7rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;margin-top:2px}
.st-chart{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:14px;margin-bottom:12px}
.st-chart-t{font-size:.85rem;font-weight:700;color:var(--text2);margin-bottom:10px}
.st-bars{display:flex;align-items:flex-end;gap:3px;height:70px}
.st-bar{flex:1;height:100%;display:flex;align-items:flex-end}
.st-bar div{width:100%;background:var(--accent);border-radius:3px 3px 0 0;min-height:2px;opacity:.85}
/* Sort tabs read as a tab bar, the same shape the Kreatr.link profile uses:
   equal segments, a quiet label, and a rule under the one you are on. The
   pill made four equally loud buttons and the filled one shouted; an
   underline says which is selected without competing with the klips. */
.psort{display:flex;gap:0;margin:4px 0 12px;border-bottom:1px solid var(--line);overflow-x:auto;scrollbar-width:none}
.psort::-webkit-scrollbar{display:none}
.psort-chip{flex:1 1 0;min-width:0;border:none;border-bottom:2px solid transparent;border-radius:0;padding:11px 4px;font-size:.85rem;font-weight:500;color:var(--text2);white-space:nowrap;text-align:center;background:none;transition:color .15s,border-color .15s}
.psort-chip.on{color:var(--text);border-bottom-color:var(--text);font-weight:600}
.st-row{display:block;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px;margin-bottom:10px}
.st-row:hover{border-color:var(--accent)}
.st-row-c{font-size:.98rem}
.st-row-m{color:var(--text2);font-size:.82rem;margin-top:5px}
.st-hl{color:var(--accent);font-size:.66rem;font-weight:800;letter-spacing:.04em}

/* Profile settings as a full-width page, not a modal. */
.settings-card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:26px;margin-bottom:16px}
.settings-card h2{margin-bottom:2px}
.settings-card > p{color:var(--text2);font-size:.92rem;margin-bottom:16px}
#app main.wrap.wide{max-width:960px}

.hdrbg-row{display:flex;gap:8px;margin:4px 0 2px}
.hdrbg-row .btn{padding:9px 14px;font-size:.88rem}
/* The live header preview at the top of settings. It is also the background
   drop zone, because the place a photo lands is the honest place to drop it. */
.pf-hero{position:relative;aspect-ratio:648/232;border-radius:14px;border:1.5px dashed var(--line);background-size:cover;background-position:center;display:flex;align-items:flex-start;justify-content:flex-end;padding:10px;cursor:pointer;margin-bottom:0;overflow:hidden}
.pf-hero:hover,.pf-hero.over{border-color:var(--accent)}
.pf-hero .hdr-hint{font-size:.72rem;color:#fff;background:rgba(0,0,0,.55);padding:4px 10px;border-radius:999px;position:relative;z-index:1}
.pf-hero-id{position:absolute;left:0;right:0;bottom:10px;display:flex;justify-content:center;pointer-events:none}
.pf-hero-id .av{width:56px;height:56px;border-radius:15px;box-shadow:0 2px 10px rgba(0,0,0,.35)}
/* The words sit under the banner, left aligned, exactly as the profile lays
   them out. XX and the joined note stand in for numbers this form cannot know. */
.pf-hero-below{display:flex;flex-direction:column;align-items:flex-start;gap:3px;margin:10px 2px 18px;text-align:left}
.pf-hero-below strong{font-size:1.15rem;letter-spacing:-0.01em}
.pf-hero-handle{color:var(--text2);font-size:.85rem;font-weight:500}
.pf-hero-bio{font-size:.9rem;line-height:1.4;margin-top:3px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pf-hero-bio:empty{display:none}
.pf-hero-link{margin-top:4px;color:var(--accent);font-size:.82rem;font-weight:700;background:var(--accent-soft);padding:4px 13px;border-radius:999px;max-width:88%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pf-hero-link.hidden{display:none}
.pf-hero-meta{color:var(--text2);font-size:.8rem;margin-top:5px}
/* Profile color swatches in the editor. */
.swatches{display:flex;flex-wrap:wrap;gap:8px;margin:4px 0 2px}
.swatch{width:30px;height:30px;border-radius:10px;border:2px solid transparent;cursor:pointer;flex-shrink:0}
.swatch.auto{background:transparent;border:2px dashed var(--line);color:var(--text2);font-size:.62rem;font-weight:800;display:flex;align-items:center;justify-content:center;letter-spacing:.02em}
.swatch.on{border-color:var(--text);box-shadow:0 0 0 2px var(--card) inset}
.swatch.auto.on{border-style:solid}
/* No color: a crossed circle, so it reads as a choice rather than an empty
   slot. Auto still means match the photo. */
.swatch.none{background:transparent;border:2px solid var(--line);display:flex;align-items:center;justify-content:center;padding:0}
.swatch.none svg{width:20px;height:20px;fill:none;stroke:#dc2626;stroke-width:2;stroke-linecap:round}
.swatch.none svg circle{stroke:var(--text2);stroke-width:1.4}

/* Desktop hover profile card. */
.hcard{position:fixed;z-index:95;width:300px;background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:0 16px 48px rgba(0,0,0,.3);overflow:hidden;opacity:0;transition:opacity .15s}
.hcard.show{opacity:1}
.hcard-banner{height:64px}
.hcard-body{padding:0 14px 14px;margin-top:-24px;position:relative}
.hcard-body .av{width:48px;height:48px;border-radius:14px;font-size:1.1rem;box-shadow:0 6px 18px rgba(0,0,0,.3)}
.hcard-name{font-weight:800;margin-top:8px}
.hcard-handle{color:var(--text2);font-size:.88rem}
.hcard-bio{color:var(--text2);font-size:.88rem;margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.hcard-counts{margin-top:8px;font-size:.9rem;color:var(--text2)}
.hcard-counts b{color:var(--text)}
.hcard-flag{position:absolute;top:8px;right:10px;color:#fff;opacity:.7;background:none;border:none;cursor:pointer}
.hcard-flag:hover{opacity:1}
.hcard-flag svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}

/* ---------- Inline replies ---------- */
/* Each reply hangs off an elbow: a line down from what it answers, turning
   across into the reply, with an arrowhead. Nesting indents, so branching
   conversations stay readable. */
.srow{display:flex;align-items:center;gap:9px;margin-top:10px}
.srow .show-replies{margin-top:0}
.show-replies{display:block;margin-top:10px;padding:4px 0;background:none;border:none;font-size:.86rem;font-weight:700;color:var(--accent);cursor:pointer;text-align:left}
/* The See more that ends the first Klip of a stack: a link in the writing,
   not another button under it. */
.see-more{display:inline;margin:0 0 0 3px;padding:0;font-size:inherit;font-weight:600;vertical-align:baseline}
.see-more.gone{display:none}
.show-replies:hover{text-decoration:underline}
#thread-modal .show-replies{display:none}
.replies{margin-top:2px}
/* Thread connectors. One spine runs down the whole set and each klip hangs
   off it on its own elbow, so a stack reads as one continuous run rather than
   a pile of separate hooks. The spine is a background on the row itself,
   which leaves both pseudo-elements free for the elbow and its arrowhead, and
   it stops at the elbow of the last row instead of running past it. */
.rrow{position:relative;padding-left:48px;padding-top:14px;
  background-image:linear-gradient(var(--from,var(--line)),var(--line));
  background-repeat:no-repeat;background-size:2px 100%;background-position:13px 0}
.rrow:last-child{background-size:2px 14px}
/* The connector is one drawn arrow: a vertical run, an eased turn and a
   head whose vertex sits on the end of the line. It is a mask, not a
   picture, so the colour still comes from the gradient underneath and the
   arrow keeps carrying the colour of whoever is being answered. */
.rrow::before{content:'';position:absolute;left:13px;top:14px;width:26px;height:30px;background:linear-gradient(var(--from,var(--line)),var(--line));-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='30'%20viewBox='0%200%2026%2030'%20fill='none'%20stroke='%23000'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M1%200V13q0%2011%2011%2011h9'/%3E%3Cpath%20d='M17%2020l4%204-4%204'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='30'%20viewBox='0%200%2026%2030'%20fill='none'%20stroke='%23000'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M1%200V13q0%2011%2011%2011h9'/%3E%3Cpath%20d='M17%2020l4%204-4%204'/%3E%3C/svg%3E");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-size:26px 30px;mask-size:26px 30px;pointer-events:none}
.rrow .rrow{padding-left:42px}

/* A thread reads as the Klip it is about: each stack post, then the replies to
   that post, collapsible so a long argument under part two does not bury the
   rest of the stack. */
.tpost{position:relative}
.tpost.replying{border-radius:var(--radius);box-shadow:0 0 0 2px var(--accent-soft)}
.rblock{margin:0}
.rblock-empty{margin:0}
/* The spine carries on THROUGH the toggle, so the run of line starts at the
   post it belongs to instead of floating below the reply count. Collapsed,
   there is nothing underneath for it to reach, so it is dropped. */
.rtoggle{display:inline-flex;align-items:center;gap:5px;border:none;color:var(--text2);font-weight:700;font-size:.84rem;padding:6px 0 6px 42px;cursor:pointer;line-height:1;background-color:transparent;background-image:linear-gradient(var(--from,var(--line)),var(--from,var(--line)));background-repeat:no-repeat;background-size:2px 100%;background-position:13px 0}
.rblock.rcollapsed > .rtoggle{background-image:none}
.rtoggle:hover{color:var(--text)}
.rtoggle-i{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;transition:transform .15s}
.rblock.rcollapsed .rtoggle-i{transform:rotate(-90deg)}
.rblock.rcollapsed > .rkids{display:none}
.rrow .rrow .rrow{padding-left:36px}
.rcard{display:flex;gap:9px;background:var(--bg);border:1px solid var(--line);border-radius:13px;padding:10px 12px;position:relative}
.rc-main{flex:1;min-width:0}
.rc-meta{font-size:.86rem}
.rc-body{margin-top:3px;font-size:.97rem;line-height:1.45;white-space:pre-wrap;word-wrap:break-word}
.rc-body:empty{display:none}

.ksrc{color:var(--text2);font-size:.78rem;margin-top:9px}
.ksrc a{color:var(--text2);text-decoration:underline;text-underline-offset:2px}
.ksrc a:hover{color:var(--text)}
/* GIF picker: a sheet that rises from the bottom of the screen, wide enough
   to browse rather than a strip tucked under the composer. */
.gif-open{border:2px solid var(--accent);color:var(--accent);background:var(--accent-soft);font-weight:800;font-size:1.15rem;letter-spacing:.04em;border-radius:12px;height:46px;min-width:74px;display:flex;align-items:center;justify-content:center;line-height:1;cursor:pointer;flex-shrink:0;padding:0 14px}
.gif-open:hover{background:var(--accent);color:#fff}
.gif-back{position:fixed;inset:0;z-index:130;background:rgba(0,0,0,.45);opacity:0;transition:opacity .22s ease}
.gif-back.up{opacity:1}
.gif-sheet{position:absolute;left:0;right:0;bottom:0;max-height:78dvh;height:78dvh;display:flex;flex-direction:column;
  background:var(--card);border-top:1px solid var(--line);border-radius:20px 20px 0 0;
  transform:translateY(100%);transition:transform .26s cubic-bezier(.22,.61,.36,1);
  padding-bottom:env(safe-area-inset-bottom)}
.gif-back.up .gif-sheet{transform:translateY(0)}
@media (min-width:760px){ .gif-sheet{left:50%;right:auto;width:620px;transform:translate(-50%,100%)} .gif-back.up .gif-sheet{transform:translate(-50%,0)} }
.gif-grip{padding:10px 0 4px;display:flex;justify-content:center;cursor:grab;touch-action:none;flex:none}
.gif-grip span{width:42px;height:5px;border-radius:999px;background:var(--line);display:block}
.gif-search{display:flex;align-items:center;gap:9px;margin:6px 14px 10px;padding:0 14px;background:var(--bg);border-radius:999px;flex:none}
.gif-search svg{width:19px;height:19px;stroke:var(--text2);fill:none;stroke-width:2;stroke-linecap:round;flex:none}
/* Specific enough to beat the thread modal's generic .modal input rule. */
.gif-search input[type="search"]{flex:1;min-width:0;border:none;background:transparent;padding:13px 0;outline:none;font-size:1rem;color:var(--text);margin:0;border-radius:0}
.gif-search input[type="search"]:focus{border:none}
/* Columns, not a grid: GIFs keep their own proportions and stagger, which is
   what makes a wall of them readable. */
.gif-grid{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:0 12px 14px;column-count:2;column-gap:8px;scrollbar-width:none}
.gif-grid::-webkit-scrollbar{display:none}
@media (min-width:560px){ .gif-grid{column-count:3} }
.gif-cell{border:none;background:var(--bg);padding:0;cursor:pointer;border-radius:12px;overflow:hidden;display:block;width:100%;margin:0 0 8px;break-inside:avoid}
.gif-cell img{width:100%;height:auto;display:block}
.gif-wait{padding:26px 4px;text-align:center;color:var(--text2);font-size:.9rem;column-span:all}
.gif-attached{position:relative;margin-top:10px;display:inline-block}
.gif-attached img{max-height:150px;border-radius:12px;display:block}
.gif-attached .cph-x{top:6px;right:6px}
/* The Alt overlay pinned to the bottom of a photo thumbnail, and its editor.
   ALT with a check means the photo already has one. */
.cph-alt{position:absolute;left:4px;right:4px;bottom:4px;border:none;border-radius:7px;background:rgba(0,0,0,.62);color:#fff;font-size:.62rem;font-weight:800;letter-spacing:.04em;padding:3px 0;cursor:pointer;backdrop-filter:blur(2px)}
.cph-alt.set{background:rgba(124,58,237,.85)}
.alt-modal{max-width:380px}
.alt-modal img{display:block;max-width:100%;max-height:200px;object-fit:contain;margin:0 auto 12px;border-radius:12px}
.alt-hint{font-size:.84rem;color:var(--text2);margin:4px 0 12px;line-height:1.45}
.alt-modal textarea{width:100%;min-height:84px;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:10px;font-size:.95rem;resize:vertical}
.alt-acts{display:flex;gap:10px;margin-top:14px}
/* Scoped past `.modal .btn-primary`, which is declared later and would
   otherwise give only the primary button that rule's width and top margin,
   leaving the pair mismatched: Save sat 18px lower than Cancel. Any
   side-by-side row of modal buttons needs this same scope. */
.modal .alt-acts .btn{flex:1;text-align:center;margin:0;width:auto;padding:12px 0;line-height:1.2}

/* Compose attachments */
.cph-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;color:var(--text2);background:none;border:none;cursor:pointer;flex-shrink:0}
.cph-btn:hover{background:var(--accent-soft);color:var(--accent)}
.cph-btn svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
/* The compose photo button is the same size as the GIF button. */
.cph-big{height:46px;min-width:74px;width:auto;border-radius:12px;border:2px solid var(--accent);background:var(--accent-soft);color:var(--accent);padding:0 14px}
.cph-big:hover{background:var(--accent);color:#fff}
.cph-big svg{width:26px;height:26px;stroke-width:2}
.cph-count{width:100%;font-size:.79rem;color:var(--text2);padding:6px 2px 0}
.cph-count strong{color:var(--text);font-weight:700}
.cph-count.full strong{color:var(--accent)}
/* Attachments and the buttons that add them share one line: the strip hugs
   its thumbnails and scrolls sideways once there are more than fit, and the
   buttons never shrink or move off the line. */
/* The desktop stand-in for the composer: a pill that opens the sheet. Hidden
   on a phone, where the plus in the tab bar does this job, and hidden on any
   tab that is not a feed. Carries the draft, so closing the sheet reads as
   minimising it. */
.compose-trigger{display:none;align-items:center;gap:11px;width:100%;text-align:left;
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:11px 14px;margin:14px 0;cursor:pointer}
.compose-trigger:hover{border-color:var(--accent)}
@media (min-width:901px){ .compose-trigger:not(.hidden){display:flex} }
.ctrig-av{flex:none;line-height:0}
.ctrig-av .av-sm{display:block}
.ctrig-text{flex:1;min-width:0;color:var(--text2);font-size:1rem;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* A draft in progress is the Klip's own words, so it reads as text, not as a
   prompt someone has yet to answer. */
.compose-trigger.has-draft .ctrig-text{color:var(--text)}
.ctrig-ico{flex:none;display:flex;color:var(--accent)}
.ctrig-ico svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}

/* Emoji, at the top right of what you are writing. Icon over a small label,
   because an outline smiley on its own is a guess. */
.compose-right{position:relative}
.emoji-btn{position:absolute;top:2px;right:12px;display:flex;flex-direction:column;align-items:center;
  gap:1px;color:var(--text2);padding:2px 4px;border-radius:10px;line-height:1}
.emoji-btn:hover{color:var(--accent);background:var(--accent-soft)}
.emoji-btn.on{color:var(--accent);background:var(--accent-soft)}
.emoji-btn svg{width:23px;height:23px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round}
.emoji-btn span{font-size:.62rem;font-weight:700;letter-spacing:.01em}
/* The first line has to clear it, and clear the stack counter beside it. */
/* Nothing sits over the words now, so the field keeps its full width. The
   emoji button and the stack chip moved up to the header beside the close,
   which is also what puts the close and the emoji on one line. */
.compose textarea{padding-right:0}
.compose.stacked #compose-text{padding-right:126px}
.compose.stacked .cpos{right:66px}

/* The picker. Beside the sheet where there is room for it, and a sheet of its
   own along the bottom where there is not. It closes on its own X and on the
   composer closing, never on a click landing anywhere else, because picking
   several emoji in a row is the ordinary way to use it. */
.emoji-pop{position:fixed;z-index:210;display:flex;flex-direction:column;
  width:312px;max-height:min(420px, 70vh);background:var(--card);
  border:1px solid var(--line);border-radius:16px;overflow:hidden;
  box-shadow:0 24px 60px -18px rgba(0,0,0,.5), 0 8px 20px -8px rgba(0,0,0,.3)}
.emoji-head{display:flex;align-items:center;justify-content:space-between;
  padding:10px 8px 10px 14px;border-bottom:1px solid var(--line);flex:none}
.emoji-head strong{font-size:.94rem;font-weight:700}
.emoji-search{display:flex;align-items:center;gap:8px;margin:8px 10px 0;padding:0 12px;
  background:var(--bg);border-radius:999px;flex:none}
.emoji-search svg{width:17px;height:17px;stroke:var(--text2);fill:none;stroke-width:2;stroke-linecap:round;flex:none}
.emoji-search input{flex:1;min-width:0;border:none;background:transparent;padding:9px 0;outline:none;font-size:.92rem}
.emoji-none{grid-column:1/-1;padding:18px 4px;text-align:center;color:var(--text2);font-size:.86rem}
/* The five most recent, on hover, so the common case never opens a panel. */
.emoji-recent{position:absolute;top:0;right:34px;z-index:6;display:flex;align-items:center;gap:2px;
  background:var(--card);border:1px solid var(--line);border-radius:999px;padding:3px 5px;
  box-shadow:0 8px 22px -8px rgba(0,0,0,.45)}
.emoji-recent button{font-size:1.15rem;line-height:1;padding:3px 4px;border-radius:8px;background:none;border:none;cursor:pointer}
.emoji-recent button:hover{background:var(--accent-soft)}
@media (hover:none){ .emoji-recent{display:none !important} }
.emoji-tabs{display:flex;gap:2px;padding:6px 8px;border-bottom:1px solid var(--line);
  overflow-x:auto;scrollbar-width:none;flex:none}
.emoji-tabs::-webkit-scrollbar{display:none}
.emoji-tab{flex:none;font-size:1.05rem;line-height:1;padding:5px 7px;border-radius:8px;opacity:.6}
.emoji-tab.on{background:var(--accent-soft);opacity:1}
.emoji-grid{flex:1;min-height:0;overflow-y:auto;padding:8px 10px 12px;
  display:grid;grid-template-columns:repeat(8,1fr);gap:2px}
.emoji-cat{grid-column:1/-1;font-size:.72rem;font-weight:700;color:var(--text2);
  text-transform:uppercase;letter-spacing:.04em;padding:8px 2px 4px}
.emoji-cell{font-size:1.35rem;line-height:1;padding:6px 0;border-radius:9px;background:none;border:none;cursor:pointer}
.emoji-cell:hover{background:var(--accent-soft)}
/* On a phone it is a bottom sheet, the same shape as the GIF picker. */
@media (max-width:760px){
  .emoji-pop{left:0;right:0;bottom:0;top:auto;width:auto;max-height:56dvh;
    border-radius:18px 18px 0 0;border-bottom:none}
  .emoji-grid{grid-template-columns:repeat(9,1fr)}
  .emoji-cell{font-size:1.5rem;padding:8px 0}
}

/* Everything that can be added to a Klip, in a named bar of its own. The
   label carries the row so the icons are not floating in the card. */
.cmedia-line{display:flex;align-items:center;gap:10px;width:100%;min-width:0;margin-top:6px;
  border:1px solid var(--line);border-radius:12px;padding:4px 5px 4px 13px}
.cmedia-label{flex:1;min-width:0;font-weight:700;font-size:.92rem;color:var(--text)}
/* Inside the bar the buttons are smaller than they were standing alone. */
.cmedia-line .cph-big,.cmedia-line .gif-open{height:32px;min-width:50px;border-radius:9px;padding:0 9px}
.cmedia-line .cph-big svg{width:19px;height:19px}
.cmedia-line .gif-open{font-size:.92rem}
.cstrip{display:flex;gap:8px;width:100%;overflow-x:auto;padding:10px 0 2px;scrollbar-width:none}
.cstrip::-webkit-scrollbar{display:none}
.cph{position:relative;flex-shrink:0;width:72px;height:72px;border-radius:12px;overflow:hidden;border:1px solid var(--line);cursor:grab}
.cph img{width:100%;height:100%;object-fit:cover;display:block}
.cph-x{position:absolute;top:3px;right:3px;width:20px;height:20px;border-radius:50%;background:rgba(0,0,0,.6);color:#fff;border:none;font-size:.85rem;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
.compose.over{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}

/* One scroll bar for the whole app, in the surface's own colours rather than
   the platform's. A bare * carries no specificity, so every strip and tab row
   that deliberately hides its bar keeps winning; this only dresses the ones
   that were showing a bright default. */
*{scrollbar-width:thin;scrollbar-color:var(--line) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px;
  border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:var(--text2);background-clip:content-box}

/* ---------- Pull to refresh ---------- */
.ptr{position:fixed;top:0;left:50%;transform:translate(-50%,-54px);z-index:85;width:44px;height:44px;border-radius:50%;background:var(--card,#fff);box-shadow:0 4px 14px rgba(0,0,0,.25);display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none}
.ptr svg{width:28px;height:28px;display:block}
.ptr-c{stroke:#9ca3af;transition:stroke .12s}
.ptr.past .ptr-c{stroke:#7c3aed}
.ptr.spin svg{animation:ptr-spin .8s linear infinite}
@keyframes ptr-spin{to{transform:rotate(360deg)}}

/* ---------- Photo Stack ---------- */
/* The frame is full width and sized to the photos it holds, never cropping
   them: JS measures the tallest photo and sets the aspect ratio, capped at
   4:5 so one very tall photo cannot take over the feed. Anything taller than
   the frame is letterboxed by object-fit:contain rather than cut. 4:5 is
   also the starting ratio, so a stack reserves its space before the first
   photo has loaded. */
/* A physical card stack: top photo full, next cards peeking beneath. ~90%
   width, centered, visible margins; the chrome only exists for 2+ photos. */
.stackwrap{margin-top:12px;position:relative;padding-bottom:24px}
.stack{position:relative;width:100%;margin:0 auto;aspect-ratio:4/5}
.scard{position:absolute;inset:0;border-radius:14px;overflow:hidden;border:1px solid rgba(128,128,128,.35);box-shadow:0 8px 24px rgba(0,0,0,.28);background:var(--card);transition:transform .28s ease,opacity .28s ease;touch-action:pan-y;cursor:grab}
.scard img{width:100%;height:100%;object-fit:contain;display:block;pointer-events:none;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-user-drag:none}
.scard.p0{transform:translateY(0) scale(1);opacity:1}
.scard.p1{transform:translateY(11px) scale(.955);opacity:1;filter:brightness(.9)}
.scard.p2{transform:translateY(20px) scale(.91);opacity:1;filter:brightness(.82)}
.scard.pback{transform:translateY(20px) scale(.91);opacity:0;pointer-events:none}
.stack-count{position:absolute;top:10px;right:10px;background:rgba(0,0,0,.55);color:#fff;font-size:.74rem;font-weight:700;padding:3px 9px;border-radius:999px;z-index:20;pointer-events:none}
.kphoto{margin-top:12px}
.kphoto img{max-width:100%;border-radius:14px;border:1px solid rgba(128,128,128,.3);display:block;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-user-drag:none}
.kgif img{max-height:280px}
.lightbox{position:fixed;inset:0;background:rgba(0,0,0,.93);z-index:120;display:flex;align-items:center;justify-content:center;touch-action:none}
.lightbox img{max-width:95vw;max-height:92vh;transform-origin:center;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;-webkit-user-drag:none}
.lb-count{position:fixed;top:18px;left:50%;transform:translateX(-50%);z-index:121;background:rgba(0,0,0,.55);color:#fff;font-size:.78rem;font-weight:700;padding:4px 11px;border-radius:999px;text-align:center;pointer-events:none}
/* The photos sit side by side on one track and the track follows the finger
   1:1, so a swipe drags the next photo into view instead of jumping to it at
   a threshold. */
.lb-track{position:absolute;top:0;left:0;height:100%;display:flex;will-change:transform}
.lb-slide{flex:none;height:100%;display:flex;align-items:center;justify-content:center}
.lightbox-x{position:fixed;top:14px;right:16px;color:#fff;font-size:2rem;line-height:1;background:none;border:none;cursor:pointer;z-index:121;padding:6px 10px}

/* Undo lives in the toast, not a confirm dialog: act now, forgive after. */
.toast.with-undo{padding:9px 9px 9px 18px;display:flex;align-items:center;gap:14px;pointer-events:auto}
.toast-undo{background:var(--bg);color:var(--accent);font-weight:700;font-size:.86rem;padding:7px 15px;border-radius:999px}
.sugg-info{flex:1;min-width:0}
.sugg-name{font-weight:700;font-size:.95rem}
.sugg-bio{color:var(--text2);font-size:.84rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.follow-btn{border:1.5px solid var(--accent);color:var(--accent);font-weight:600;border-radius:999px;padding:6px 16px;font-size:.86rem;flex-shrink:0}
.follow-btn.on{background:var(--accent);color:#fff}

/* Follow-from-the-avatar: a circled plus pinned to the bottom-left corner of
   any profile picture the viewer does not follow. Tapping it follows, flashes
   a Followed overlay over the picture, and the plus goes away. */
.av-wrap{position:relative;display:inline-flex;flex-shrink:0;align-self:flex-start}
.av-follow{position:absolute;bottom:-5px;left:-5px;width:20px;height:20px;border-radius:50%;background:var(--accent);display:flex;align-items:center;justify-content:center;border:2px solid var(--card);box-shadow:0 1px 4px rgba(0,0,0,.3);z-index:2;cursor:pointer}
.av-follow svg{width:11px;height:11px;stroke:#fff;stroke-width:2.6;fill:none;stroke-linecap:round}
.av-follow:active{transform:scale(.88)}
.av-done{position:absolute;inset:0;border-radius:13px;background:rgba(124,58,237,.9);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;opacity:0;transition:opacity .25s;pointer-events:none;z-index:3}
.av-wrap .av-xs+.av-done,.av-wrap.xs .av-done{border-radius:11px}
.av-done.show{opacity:1}
.av-done svg{width:13px;height:13px;stroke:#fff;stroke-width:2.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
.av-done span{font-size:7px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:#fff}
.empty .btn{padding:9px 20px;font-size:.9rem;margin:14px 5px 0}
.group-grid{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-top:12px}
.group-chip{border:1.5px solid var(--line);border-radius:999px;padding:7px 15px;font-size:.88rem;font-weight:600;color:var(--text)}
.group-chip.on{background:var(--accent);border-color:var(--accent);color:#fff}

/* Modal */
.modal-back{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:50;display:flex;align-items:center;justify-content:center;padding:16px;overscroll-behavior:contain}
/* The composer opens where the reader was already looking: its top edge on
   the top edge of the first Klip, rather than centred in the window and a
   hair off everything behind it. JS sets the offset, since only it knows
   where the feed starts. */
@media (min-width:761px){
  #compose-modal{align-items:flex-start}
  #compose-modal .compose-box{margin-top:var(--sheet-top, 0px)}
}
/* A modal taller than the screen scrolls inside itself; the page behind it
   never moves. */
.modal{background:var(--card);border-radius:20px;padding:24px;width:100%;max-width:400px;max-height:calc(100svh - 32px);overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
.modal h2{font-size:1.25rem;margin-bottom:4px}
.modal p{color:var(--text2);font-size:.9rem;margin-bottom:16px}
/* The settings page hosts the same form the modal used to, so it needs the
   same field treatment. */
.modal label,.settings-card label{display:block;font-size:.82rem;font-weight:600;color:var(--text2);margin:12px 0 5px}
.modal input,.settings-card input:not([type=file]):not([type=checkbox]){width:100%;border:1.5px solid var(--line);border-radius:11px;padding:11px 13px;background:transparent;outline:none;font-size:1rem;color:var(--text)}
.modal input:focus,.settings-card input:focus{border-color:var(--accent)}
.field-note{font-size:.8rem;margin-top:5px;min-height:1em;color:var(--text2)}
.field-note.ok{color:#16a34a}
.field-note.bad{color:var(--red)}
.modal .btn-primary{width:100%;margin-top:18px;text-align:center}
.modal .btn-primary:disabled{opacity:.5}
.settings-card .btn-primary{width:100%;margin-top:18px;text-align:center}
.settings-card .btn-primary:disabled{opacity:.5}

.thread-box{max-width:560px;max-height:86vh;overflow-y:auto;padding:16px}
.thread-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}
.thread-head h2{font-size:1.1rem}
.x-btn{font-size:1.5rem;color:var(--text2);line-height:1;padding:4px 10px}
.reply-note{color:var(--text2);font-size:.85rem;margin:10px 0 6px}
/* The foot had nothing under it, so the GIF button sat right on top of the
   next card in the thread. */
.reply-compose{display:flex;gap:10px;margin:12px 0 6px;border-top:1px solid var(--line);padding:14px 0 16px}
.reply-compose .compose-right{flex:1;position:relative}
/* Room for the Emoji button at the top right, the way the composer has. */
.reply-compose textarea{width:100%;border:none;outline:none;background:transparent;resize:none;font-size:1rem;line-height:1.4;min-height:44px;padding-right:52px}
.reply-compose .emoji-btn{top:-2px}
.reply-compose .compose-foot{margin-top:10px}
/* The thread panel scrolls, so its own bar runs down this edge. The count and
   the Reply button keep clear of it rather than sitting under it. */
.reply-compose .compose-right{padding-right:10px}
.thread-replies .clip{border-left:3px solid var(--accent-soft)}
.disc-note{background:var(--accent-soft);color:var(--accent);border-radius:var(--radius);padding:11px 14px;font-size:.86rem;font-weight:600;margin-bottom:12px}
.loadmore{text-align:center;color:var(--text2);padding:18px 0 40px;font-size:.9rem}
.share-modal{max-width:440px;text-align:center}
.share-modal h2{margin-bottom:14px}
.share-preview{width:100%;max-width:300px;margin:0 auto 18px;border-radius:14px;overflow:hidden;border:1px solid var(--line);background:var(--bg)}
.share-preview img{display:block;width:100%;height:auto}
/* Both buttons are the same box. .modal .btn-primary makes a primary button
   full width with a top margin, which is right for a form and wrong for a
   pair side by side, so it is undone here. */
.share-actions{display:flex;gap:10px;align-items:stretch}
.share-actions .btn{flex:1 1 0;width:auto;margin:0;text-align:center;padding:12px 0;line-height:1.2;min-height:0}
.share-busy{color:var(--text2);font-size:.9rem;padding:40px 0}
.a2hs{position:fixed;left:12px;right:12px;bottom:12px;z-index:55;display:flex;align-items:center;gap:12px;background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.22);padding:12px 14px;max-width:520px;margin:0 auto}
.a2hs-icon{width:42px;height:42px;border-radius:11px;flex-shrink:0}
.a2hs-txt{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.a2hs-txt strong{font-size:.94rem}
.a2hs-txt span{font-size:.82rem;color:var(--text2);line-height:1.35}
.a2hs-acts{display:flex;align-items:center;gap:4px;flex-shrink:0}
.a2hs .btn{padding:9px 16px;font-size:.9rem}
.a2hs-x{color:var(--text2);font-size:1.35rem;line-height:1;padding:4px 8px;border-radius:8px}
.a2hs-x:hover{background:var(--line);color:var(--text)}
@media (max-width:900px){.a2hs{bottom:74px}}
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--text);color:var(--bg);padding:10px 20px;border-radius:999px;font-size:.9rem;z-index:60;opacity:0;transition:opacity .25s;pointer-events:none;max-width:calc(100vw - 28px);white-space:nowrap}
/* Clears the bottom bar, same as the install banner. */
@media (max-width:900px){ .toast{bottom:78px} }
.toast.show{opacity:1}

/* ---------- Skeletons ---------- */
/* One primitive. Every shape is sizes on top of it. The bar tints from the
   page's own text color, so it inverts with the theme by itself: no media
   query, and no way to paint white bars onto a white page when the OS
   preference and the chosen theme disagree. */
.kb{position:relative;overflow:hidden;border-radius:7px;background:#e4e7ec;background:color-mix(in srgb, currentColor 11%, transparent)}
.kb::after{content:"";position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,rgba(255,255,255,.34),transparent);animation:kshine 1.25s ease-in-out infinite}
@keyframes kshine{100%{transform:translateX(100%)}}
@media (prefers-reduced-motion: reduce){ .kb::after{animation:none} }
.kskel{pointer-events:none}
.krow{display:flex;align-items:center;gap:11px}
.kcol{flex:1;min-width:0;display:flex;flex-direction:column;gap:7px}
/* Boot outline: the real layout classes, so the shapes land where the content
   will. Shown only while a cached session says this reader is signed in. */
#boot{display:none}
html.sess-pending #boot{display:block}
#boot main.wrap{padding-top:70px}
@media (max-width:900px){ #boot main.wrap{padding-top:64px} }
#boot .kn{display:flex;align-items:center;gap:12px;padding:11px 13px}
@media (max-width:900px){ #boot .kn{flex-direction:column;gap:5px;padding:6px 4px;flex:1} #boot .boot-logo{display:none} }

/* The sphere that marks where you are. It rides a slot index rather than a
   measured offset: the buttons share the bar equally, so "which fifth" is the
   whole answer and it cannot be wrong because the bar had no dimensions yet
   when the tab was set. Desktop keeps the plain sidebar, so it never renders.*/
.navball{display:none}
@media (max-width:900px){
  /* One number sets the rise. The icon moves with it, because a sphere whose
     centre sits above the icon it is carrying reads as floating away from
     the bar rather than sitting proud of it. Icon centre is 24px down (half
     of a 48px button, whatever its padding), the sphere's is 23px minus the
     lift,
     so the icon travels the difference: -1px minus the lift. */
  .snav{--nav-lift:10px}
  .navball{display:block;position:absolute;top:calc(0px - var(--nav-lift));left:0;height:46px;
    width:calc(100% / var(--navn,5));pointer-events:none;z-index:1;
    transform:translateX(calc(var(--navi,0) * 100%));
    transition:transform .36s cubic-bezier(.34,1.26,.52,1)}
  .navball::after{content:'';position:absolute;top:0;left:50%;margin-left:-23px;
    width:46px;height:46px;border-radius:50%;
    background:radial-gradient(circle at 34% 26%,
      color-mix(in srgb, var(--accent) 45%, #fff) 0%,
      var(--accent) 56%,
      color-mix(in srgb, var(--accent) 70%, #000) 100%);
    box-shadow:0 14px 22px -8px color-mix(in srgb, var(--accent) 65%, transparent),
               0 2px 6px rgba(0,0,0,.2)}
  @media (prefers-reduced-motion: reduce){ .navball{transition:none} }
}

/* The composer sheet. It sits in the middle of the screen with room around it,
   so the shadow has somewhere to fall and the sheet reads as lifted off the
   feed rather than pasted onto it. Inside it the composer is the whole
   surface, so it drops the card border and the margins it needed in the feed. */
#compose-modal{align-items:center;padding:28px 22px calc(28px + env(safe-area-inset-bottom))}
.compose-box{width:100%;max-width:600px;background:var(--card);border-radius:var(--radius);
  display:flex;flex-direction:column;overflow:hidden;
  /* Hugs its content rather than always standing 560px tall: with the rows
     no longer padded out by stretch, a fixed height was visible dead space
     under the attachments bar. */
  max-height:min(560px, calc(100vh - 120px));
  box-shadow:0 28px 64px -16px rgba(0,0,0,.5), 0 10px 24px -8px rgba(0,0,0,.28);
  transition:transform .22s cubic-bezier(.32,.72,0,1)}
.compose-box.dragging{transition:none}
/* The grabber says the sheet can be pulled down before anyone tries it. */
.compose-head{display:flex;align-items:center;gap:10px;
  padding:10px 14px;border-bottom:1px solid var(--line);flex:none;
  cursor:grab;touch-action:none;position:relative;user-select:none;-webkit-user-select:none}
.compose-head:active{cursor:grabbing}
.compose-head::before{content:'';position:absolute;top:6px;left:50%;margin-left:-18px;
  width:36px;height:4px;border-radius:999px;background:var(--line2,var(--line))}
.compose-head #compose-av{flex:none;line-height:0}
/* Add post sits where the title was: the sheet is plainly the sheet, and the
   title was a row of chrome spent saying so. The auto margin holds the close
   button at the right edge whether or not Add post is showing. */
.compose-head .cadd{flex:none;margin-right:auto}
.compose-head .x-btn{flex:none;margin-left:0}
/* Emoji and close share the right end of the header, on one line. */
.compose-head .emoji-btn{position:static;flex:none;margin-left:auto;padding:2px 6px}
.compose-head .cpos{position:static;flex:none}
.compose-box .compose{margin:0;border:none;border-radius:0;background:transparent;
  overflow-y:auto;flex:1;min-height:0;
  scrollbar-width:thin;scrollbar-color:var(--line) transparent}
/* The sheet's own bar, the tall one that ran down past the Emoji button. Same
   treatment as the field's: thin, the card's line colour, no track. */
.compose-box .compose::-webkit-scrollbar{width:10px}
.compose-box .compose::-webkit-scrollbar-track{background:transparent}
.compose-box .compose::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px;
  border:3px solid transparent;background-clip:content-box}
.compose-box .compose:hover::-webkit-scrollbar-thumb{background:var(--text2);background-clip:content-box}
/* The five line cap is the ceiling everywhere; the sheet does not override it. */
.compose-box .compose textarea{min-height:104px;flex:0 0 auto}
@media (prefers-reduced-motion: reduce){ .compose-box{transition:none} }

/* ---------- Voice notes ---------- */
/* Recording happens in the Klip being written, never in a window of its own,
   so this sits in the composer between the field and the buttons. */
.vrec{margin-top:10px;border:1px solid var(--line);border-radius:12px;padding:10px 12px;background:var(--bg)}
/* The native player carries its own intrinsic width, so the row is allowed to
   wrap and every child may shrink. Without this the note ran off the side of
   the sheet and had to be scrolled to. */
.vrec-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
.vrec,.vrec-row > *{min-width:0;max-width:100%}
.vrec-dot{width:9px;height:9px;border-radius:50%;background:#e0245e;flex:none;animation:vrecpulse 1.1s ease-in-out infinite}
@keyframes vrecpulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.82)}}
/* Kreatr's own voice silhouette, so the two apps agree on what speech looks like. */
/* The sound as it happens. The bars are driven by the microphone rather than by
   a keyframe: nothing here animates on its own, so silence looks like silence
   and a loud word looks loud. Same bars as the card that plays it back, so what
   you watch while recording is what you get. */
.vrec-wave{flex:1 1 160px;min-width:0;height:30px;display:flex;align-items:center;
  gap:2px;overflow:hidden}
.vrec-wave i{flex:1 1 0;min-width:2px;background:var(--accent);border-radius:999px;
  height:6%;transition:height .07s linear}
@media (prefers-reduced-motion: reduce){ .vrec-wave i{transition:none} }
.vrec-time{font-variant-numeric:tabular-nums;font-weight:700;font-size:.9rem;flex:none}
.vrec-stop,.vrec-redo{flex:none;border:1px solid var(--line2,var(--line));background:var(--card);
  color:var(--text);border-radius:999px;padding:6px 14px;font-weight:700;font-size:.82rem;cursor:pointer;text-align:center}
.vrec-stop:hover,.vrec-redo:hover{border-color:var(--accent);color:var(--accent)}
.vrec-audio{flex:1 1 190px;min-width:0;max-width:100%;height:34px}
.vrec-len{font-variant-numeric:tabular-nums;font-size:.82rem;color:var(--text2);flex:none}
.vrec-note{margin:7px 0 0;font-size:.78rem;color:var(--text2);overflow-wrap:anywhere}
.vrec-tr{color:var(--text);font-size:.86rem;line-height:1.45}
@media (prefers-reduced-motion: reduce){
  .vrec-dot,.vrec-wave i{animation:none}
  .vrec-wave i{transform:scaleY(.7);opacity:.6}
}

/* A spoken Klip in the feed. The transcript is real text under the player, so
   it is read by a screen reader and indexed by a search engine rather than
   living in an attribute nobody can see. */
.kvoice{display:flex;align-items:center;gap:10px;margin-top:10px;
  border:1px solid var(--line);border-radius:12px;padding:9px 12px;background:var(--bg)}
.kvoice-a{flex:1;min-width:0;height:34px}
.kvoice-len{font-variant-numeric:tabular-nums;font-size:.82rem;color:var(--text2);flex:none}
/* Five lines, then it scrolls. 1.5 x .92rem is 22.1px a line, so five lines
   and the top margin is 110px. The bar is the same thin one the composer uses;
   there is one scrollbar in this app and this is it. */
.kvoice-tr{margin:8px 0 0;font-size:.92rem;line-height:1.5;color:var(--text2);
  font-style:italic;max-height:110px;overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:var(--line) transparent}
.kvoice-tr::-webkit-scrollbar{width:8px}
.kvoice-tr::-webkit-scrollbar-track{background:transparent}
.kvoice-tr::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px;
  border:2px solid transparent;background-clip:content-box}
.kvoice-tr:hover::-webkit-scrollbar-thumb{background:var(--text2);background-clip:content-box}
/* The last line of a transcript is always the same line: the machine that
   wrote it is not reliable and the reader should know before they quote it. */
.kvoice-note{display:block;margin-top:6px;font-size:.76rem;font-style:normal;
  color:var(--text2);opacity:.8}

/* A voice card before it is a player: a button, what it is, how long it runs. */
.kvoice-play{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;
  background:var(--accent);color:#fff}
.kvoice-play svg{width:18px;height:18px;fill:currentColor;stroke:none}
.kvoice-locked .kvoice-play{background:var(--accent-soft);color:var(--accent);cursor:pointer}
.kvoice-locked .kvoice-play svg{fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.kvoice-lab{flex:1;min-width:0;font-size:.88rem;font-weight:600;color:var(--text2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kvoice-play:disabled{opacity:.6;cursor:default}

/* Sending, and failing without losing anything. */
.vrec-lab{flex:1;min-width:0;font-size:.86rem;font-weight:600;color:var(--text2);
  overflow:hidden;text-overflow:ellipsis}
.vrec-bad{color:var(--danger,#c0392b);font-weight:700}
.vrec-track{height:6px;border-radius:999px;background:var(--line);overflow:hidden;margin-top:8px}
.vrec-fill{height:100%;background:var(--accent);border-radius:999px;transition:width .2s ease-out}
@media (prefers-reduced-motion: reduce){ .vrec-fill{transition:none} }

/* A transcript is a way in, not the Klip itself: two lines and a way to open
   the rest, so a long one never buries the card under it. */
.kvoice-tr.open{display:block;-webkit-line-clamp:unset}

/* The bars are the player. The played part is a gradient stop rather than a
   second set of elements, so filling is one custom property changing and never
   fifty-six style writes a second. */
.kwave{flex:1;min-width:0;height:30px;display:flex;align-items:center;gap:2px;
  cursor:pointer;touch-action:none;--played:0%}
.kwave i{flex:1 1 0;min-width:2px;border-radius:999px;
  background:linear-gradient(to right, var(--accent) 0 var(--fill,0%), var(--line) var(--fill,0%) 100%)}
.kwave:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}
.kvoice-busy .kwave{opacity:.5}
.kvoice-locked .kwave{cursor:default;opacity:.65}
.kvoice-playing .kvoice-play svg{transform:scale(.9)}

/* The microphone is held, so the browser must not treat the hold as a gesture
   of its own. Without touch-action a long press scrolls the sheet, and without
   the callout and selection off, iOS pops the text-selection menu over the top
   of a recording that is already running. */
#voice-btn,#reply-voice-btn{touch-action:none;-webkit-touch-callout:none;
  -webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}
#voice-btn:active,#reply-voice-btn:active{color:var(--accent);transform:scale(.94)}
@media (prefers-reduced-motion: reduce){
  #voice-btn:active,#reply-voice-btn:active{transform:none}
}
