/* DevSocial design system — verbatim from ui-samples/6.html */

  :root{
    --canvas:#E1E5EB;
    --card:#FFFFFF;
    --border:#CDD5DE;
    --border-strong:#B4BDC8;
    --ink:#1F2328;
    --muted:#656D76;
    --faint:#8C959F;
    --accent:#0969DA;
    --accent-hover:#0860CA;
    --accent-wash:#DDF4FF;
    --green:#1A7F37;
    --green-bg:#DAFBE1;
    --purple:#8250DF;
    --amber:#9A6700;
    --amber-bg:#FFF8C5;
    --danger:#CF222E;
    --surface-mono:#F6F8FA;
    --radius:6px;
    --radius-sm:4px;
    --shadow:0 1px 0 rgba(31,35,40,0.04);
    --shadow-pop:0 8px 24px rgba(31,35,40,0.12);
    --ff-ui:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
    --ff-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation Mono',monospace;
  }

  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;}
  body{
    font-family:var(--ff-ui);
    background:var(--canvas);
    color:var(--ink);
    font-size:14px;
    line-height:1.5;
    -webkit-font-smoothing:antialiased;
    text-rendering:optimizeLegibility;
    overflow-x:hidden;
  }
  a{color:inherit;text-decoration:none;}
  button{font-family:inherit;cursor:pointer;}
  img{max-width:100%;display:block;}
  .mono{font-family:var(--ff-mono);}

  :focus-visible{
    outline:2px solid var(--accent);
    outline-offset:2px;
    border-radius:3px;
  }
  ::selection{background:var(--accent-wash);}

  /* ---------- TOP BAR ---------- */
  .topbar{
    position:sticky;top:0;z-index:60;
    background:rgba(255,255,255,0.92);
    backdrop-filter:saturate(180%) blur(8px);
    border-bottom:1px solid var(--border);
  }
  .topbar-inner{
    max-width:1200px;margin:0 auto;
    display:flex;align-items:center;gap:16px;
    height:56px;padding:0 20px;
  }
  .hamburger{display:none;}

  .logo{
    display:flex;align-items:center;gap:9px;
    font-weight:800;font-size:19px;letter-spacing:-0.02em;
    color:var(--ink);flex:none;
  }
  .logo-mark{
    width:28px;height:28px;border-radius:6px;flex:none;
    background:linear-gradient(135deg,var(--accent),#2C7BE5);
    display:grid;place-items:center;
    box-shadow:inset 0 0 0 1px rgba(255,255,255,0.18);
  }
  .logo b{color:var(--accent);}

  .search{
    position:relative;flex:1 1 auto;min-width:0;max-width:400px;
    display:flex;align-items:center;
  }
  .search svg{position:absolute;left:11px;color:var(--faint);pointer-events:none;}
  .search input{
    width:100%;height:36px;
    padding:0 60px 0 34px;
    font-family:var(--ff-ui);font-size:13px;color:var(--ink);
    background:var(--surface-mono);
    border:1px solid var(--border);
    border-radius:var(--radius);
    transition:border-color .12s,background .12s,box-shadow .12s;
  }
  .search input::placeholder{color:var(--faint);}
  .search input:focus{
    outline:none;background:#fff;border-color:var(--accent);
    box-shadow:0 0 0 3px var(--accent-wash);
  }
  .kbd{
    position:absolute;right:8px;top:50%;transform:translateY(-50%);
    font-family:var(--ff-mono);font-size:11px;font-weight:600;
    color:var(--muted);background:#fff;
    border:1px solid var(--border-strong);border-radius:5px;
    padding:2px 6px;line-height:1;pointer-events:none;
  }

  .topnav{display:flex;align-items:center;gap:2px;margin-left:auto;flex:none;}
  .topnav-item{
    position:relative;
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
    min-width:56px;height:44px;padding:0 6px;
    color:var(--muted);font-size:11px;font-weight:500;
    /* buttons default to a light UA "buttonface" fill — reset it so they don't show a white box in dark mode */
    background:none;border:none;cursor:pointer;
    border-radius:var(--radius-sm);
    transition:color .12s,background .12s;
  }
  .topnav-item:hover{color:var(--ink);background:var(--surface-mono);}
  /* hudr.net — the one coloured item in the header. Every other control here is muted grey, so
     accent is what makes "Install" read as an offer instead of another navigation icon. */
  #installAppBtn{color:var(--accent);font-weight:600}
  #installAppBtn:hover{color:var(--accent);background:color-mix(in srgb, var(--accent) 12%, transparent)}
  #installAppBtn svg{stroke-width:2}
  .topnav-item svg{stroke-width:1.8;}
  .badge{
    position:absolute;top:3px;right:9px;
    min-width:16px;height:16px;padding:0 4px;
    background:var(--danger);color:#fff;
    font-family:var(--ff-mono);font-size:10px;font-weight:700;line-height:16px;
    text-align:center;border-radius:999px;
    box-shadow:0 0 0 2px var(--card);
  }
  .dot-badge{
    position:absolute;top:7px;right:15px;
    width:8px;height:8px;background:var(--green);
    border-radius:50%;box-shadow:0 0 0 2px var(--card);
  }

  .avatar-menu{position:relative;margin-left:6px;}
  .avatar-btn{
    display:flex;align-items:center;gap:5px;
    padding:3px;background:none;border:1px solid transparent;border-radius:999px;
    transition:border-color .12s;
  }
  .avatar-btn:hover{border-color:var(--border-strong);}
  .avatar-btn .caret{color:var(--muted);}

  .avatar{
    display:grid;place-items:center;flex:none;
    border-radius:50%;color:#fff;font-weight:700;
    font-size:13px;letter-spacing:0.01em;overflow:hidden;
    background:#6E7781;
  }
  .avatar.sm{width:32px;height:32px;font-size:12px;}
  .avatar.md{width:40px;height:40px;}
  .avatar.lg{width:52px;height:52px;font-size:17px;}
  .a-blue{background:linear-gradient(135deg,#0969DA,#54AEFF);}
  .a-green{background:linear-gradient(135deg,#1A7F37,#4AC26B);}
  .a-purple{background:linear-gradient(135deg,#8250DF,#C297FF);}
  .a-amber{background:linear-gradient(135deg,#BC4C00,#FB8F44);}
  .a-pink{background:linear-gradient(135deg,#BF3989,#FF80C8);}
  .a-slate{background:linear-gradient(135deg,#424A53,#6E7781);}
  .a-teal{background:linear-gradient(135deg,#0C7C74,#39C5BB);}

  .dropdown{
    position:absolute;right:0;top:calc(100% + 8px);
    width:264px;background:var(--card);
    border:1px solid var(--border);border-radius:var(--radius);
    box-shadow:var(--shadow-pop);
    padding:6px;opacity:0;visibility:hidden;transform:translateY(-4px);
    transition:opacity .12s,transform .12s,visibility .12s;z-index:70;
  }
  .dropdown.open{opacity:1;visibility:visible;transform:translateY(0);}
  .dd-head{display:flex;gap:10px;align-items:center;padding:10px 8px;border-bottom:1px solid var(--border);margin-bottom:6px;}
  .dd-head .nm{font-weight:600;font-size:14px;line-height:1.2;}
  .dd-head .hd{font-family:var(--ff-mono);font-size:12px;color:var(--muted);margin-top:2px;}
  .dd-item{
    display:flex;align-items:center;gap:10px;width:100%;
    padding:8px 8px;border:none;background:none;border-radius:var(--radius-sm);
    color:var(--ink);font-size:13px;text-align:left;
    transition:background .12s;
  }
  .dd-item svg{color:var(--muted);stroke-width:1.8;flex:none;}
  .dd-item:hover{background:var(--surface-mono);}
  .dd-item.danger{color:var(--danger);}
  .dd-item.danger svg{color:var(--danger);}
  .dd-sep{height:1px;background:var(--border);margin:6px 0;}
  .dd-sw{margin-left:auto;font-family:var(--ff-mono);font-size:11px;color:var(--muted);border:1px solid var(--border);border-radius:5px;padding:1px 6px;}

  /* ---------- LAYOUT ---------- */
  .layout{
    max-width:1200px;margin:0 auto;
    display:grid;
    grid-template-columns:240px minmax(0,1fr) 312px;
    gap:24px;
    padding:24px 20px calc(72px + env(safe-area-inset-bottom));
    align-items:start;
  }
  .card{
    background:var(--card);
    border:1px solid var(--border);
    border-radius:var(--radius);
    box-shadow:var(--shadow);
  }

  /* ---------- SIDEBAR ---------- */
  .sidebar{position:sticky;top:80px;display:flex;flex-direction:column;gap:16px;}

  .identity{overflow:hidden;}
  .identity-cover{height:56px;background:
     linear-gradient(120deg,#0969DA22,#1A7F3722),
     repeating-linear-gradient(90deg,#0000 0 11px,#E1E4E866 11px 12px);
     border-bottom:1px solid var(--border);}
  .identity-body{padding:0 16px 16px;}
  .identity .avatar{margin-top:-26px;border:3px solid var(--card);}
  .identity .name{font-weight:700;font-size:16px;margin-top:8px;letter-spacing:-0.01em;}
  .identity .handle{font-family:var(--ff-mono);font-size:12px;color:var(--accent);}
  .identity .headline{color:var(--muted);font-size:12.5px;margin-top:6px;line-height:1.45;}
  .identity-metrics{
    display:flex;gap:14px;margin-top:14px;padding-top:14px;border-top:1px solid var(--border);
  }
  .metric .n{font-family:var(--ff-mono);font-weight:700;font-size:14px;color:var(--ink);}
  .metric .l{font-size:11px;color:var(--muted);}
  .metric .n b{color:var(--green);}

  .sidenav{padding:6px;}
  .nav-item{
    display:flex;align-items:center;gap:11px;
    padding:9px 10px;border-radius:var(--radius-sm);
    color:var(--muted);font-size:13.5px;font-weight:500;
    position:relative;transition:background .12s,color .12s;
  }
  .nav-item svg{stroke-width:1.8;flex:none;}
  .nav-item:hover{background:var(--surface-mono);color:var(--ink);}
  .nav-item .count{margin-left:auto;font-family:var(--ff-mono);font-size:11px;color:var(--muted);background:var(--surface-mono);border:1px solid var(--border);border-radius:999px;padding:0 7px;}
  .nav-item.active{color:var(--accent);background:var(--accent-wash);font-weight:600;}
  .nav-item.active svg{color:var(--accent);}
  .nav-item.active::before{
    content:"";position:absolute;left:-6px;top:8px;bottom:8px;width:3px;
    background:var(--accent);border-radius:0 3px 3px 0;
  }

  .btn{
    display:inline-flex;align-items:center;justify-content:center;gap:7px;
    font-weight:600;font-size:13.5px;
    border-radius:var(--radius-sm);border:1px solid transparent;
    padding:9px 14px;transition:background .12s,border-color .12s,color .12s,box-shadow .12s;
    white-space:nowrap;
  }
  .btn svg{stroke-width:2;}
  .btn-primary{background:var(--accent);color:#fff;border-color:var(--accent);}
  .btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);}
  .btn-ghost{background:#fff;color:var(--ink);border-color:var(--border-strong);}
  .btn-ghost:hover{background:var(--surface-mono);border-color:var(--faint);}
  .btn-outline{background:transparent;color:var(--ink);border-color:var(--border-strong);}
  .btn-outline:hover{background:var(--surface-mono);border-color:var(--accent);color:var(--accent);}
  .btn-green{background:var(--green);color:#fff;border-color:var(--green);}
  .btn-green:hover{filter:brightness(1.05);}
  .btn-block{width:100%;}
  .btn-sm{padding:6px 12px;font-size:12.5px;}

  .create-cta{padding:14px 16px;}
  .create-cta p{margin:0 0 10px;font-size:12px;color:var(--muted);}

  /* ---------- FEED ---------- */
  .feed{display:flex;flex-direction:column;gap:16px;min-width:0;}

  .feed-tabs{
    display:flex;align-items:center;gap:4px;
    background:var(--card);border:1px solid var(--border);border-radius:var(--radius);
    padding:5px;box-shadow:var(--shadow);
    overflow-x:auto;scrollbar-width:none;
  }
  .feed-tabs::-webkit-scrollbar{display:none;}
  .tab{
    display:inline-flex;align-items:center;gap:6px;white-space:nowrap;
    padding:7px 12px;border-radius:var(--radius-sm);border:none;background:none;
    font-family:var(--ff-mono);font-size:12.5px;font-weight:500;color:var(--muted);
    transition:background .12s,color .12s;
  }
  .tab:hover{background:var(--surface-mono);color:var(--ink);}
  .tab.active{background:var(--ink);color:var(--card);}
  .tab .fdot{width:6px;height:6px;border-radius:50%;background:var(--green);}
  .tabs-spacer{margin-left:auto;}
  .tab-icon{padding:7px 9px;color:var(--muted);}

  .composer{padding:14px 16px;}
  .composer-row{display:flex;gap:12px;align-items:center;}
  .composer-input{
    flex:1;height:42px;padding:0 14px;
    background:var(--surface-mono);border:1px solid var(--border);border-radius:999px;
    color:var(--muted);font-size:13.5px;text-align:left;
    transition:border-color .12s,background .12s;
  }
  .composer-input:hover{border-color:var(--border-strong);background:#fff;}
  .composer-actions{
    display:flex;gap:4px;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);
    flex-wrap:wrap;
  }
  .chip-action{
    display:inline-flex;align-items:center;gap:7px;
    padding:7px 11px;border-radius:var(--radius-sm);border:none;background:none;
    color:var(--muted);font-size:12.5px;font-weight:500;transition:background .12s,color .12s;
  }
  .chip-action:hover{background:var(--surface-mono);}
  .chip-action.c1 svg{color:var(--green);} .chip-action.c1:hover{color:var(--green);}
  .chip-action.c2 svg{color:var(--accent);} .chip-action.c2:hover{color:var(--accent);}
  .chip-action.c3 svg{color:var(--purple);} .chip-action.c3:hover{color:var(--purple);}
  .chip-action.c4 svg{color:var(--amber);} .chip-action.c4:hover{color:var(--amber);}
  .composer-actions .btn{margin-left:auto;}

  /* ---------- POST ---------- */
  .post{padding:16px;}
  /* Considered hover — feed cards only. Both tokens are theme-driven, so dark mode needs no
     extra rule: --border-strong is the one step up from --border in either palette. */
  .card.post{transition:border-color .12s, box-shadow .12s;}
  .card.post:hover{border-color:var(--border-strong);}
  .post-flag{
    display:inline-flex;align-items:center;gap:6px;
    font-family:var(--ff-mono);font-size:11px;font-weight:600;color:var(--muted);
    margin:-4px 0 10px;
  }
  .post-flag svg{stroke-width:2;}
  .post-flag b{color:var(--ink);font-weight:600;}
  /* hudr.net (post review) — "In review" pill, shown to the AUTHOR of a post that is still
     waiting on a moderator (window.hudrReviewFlag in /ux/review-notice.js). It is the same
     .post-flag row the community flag, "Promoted" and "Saved" already use — only the colour
     differs, and it is the calm accent rather than a warning amber on purpose: a post in
     review is the normal path, not a problem the author has to act on. Both tokens are
     theme-driven, so dark mode needs no extra rule. */
  .post-flag.review-flag{color:var(--accent);}
  .post-flag.review-flag b{color:var(--accent);}
  .post-flag.review-flag span{color:var(--muted);}
  .post-head{display:flex;gap:11px;align-items:flex-start;}
  .ph-main{min-width:0;flex:1;}
  /* Identity row. The collaborator byline (.hcol-in — styled by collab.js, not here) is
     concatenated INSIDE this row after the verified badge, so the row has to stay a single
     line that truncates rather than one that reflows onto a second line.
     Shrink chain as the viewport narrows, first to give way → last:
       1. the byline words        — .hcol-in is flex:0 3 auto, so it gives way 3x faster
       2. the author's name       — ellipsises; weight 1 keeps it the widest thing on the row
       3. never shrinks           — the 18px discs, the verified badge, the ⋯, the 40px avatar
     The timestamp lives on .ph-sub (row 2, already nowrap+ellipsis) and .post-menu is a
     flex:none sibling of a min-width:0 .ph-main, so neither can be pushed off screen at 360px.
     Side effect, intended: a very long author name now ellipsises on one line on EVERY post
     instead of wrapping under itself — identity is always exactly one row. */
  .ph-name{display:flex;align-items:center;gap:7px;flex-wrap:nowrap;min-width:0;}
  .ph-name > a.au-link{min-width:0;display:inline-flex;align-items:center;}
  /* min-width:0 (not a px floor): min-width clamps a flex item UPWARD as well as down, so a
     hard floor here would pad dead space after every short display name. The 3:1 shrink
     weighting already keeps the author the loudest thing on the row. */
  .ph-name .n{
    font-weight:600;font-size:14px;color:var(--ink);letter-spacing:-0.01em;
    min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  }
  /* Floor the byline at the width of the discs it carries. collab.js ships .hcol-in{min-width:0},
     which lets flexbox shrink the button narrower than its own flex:none discs — measured at up
     to 30px of discs painting OUTSIDE the button, straight over the ⋯, at 360px. 36px = two 18px
     discs overlapped -6px (30) + the 6px gap before the words; it is a floor, so it is inert at
     every width where the byline is not already crushed, and it hands the last of the squeeze to
     the name's ellipsis — which is the documented intent, discs survive last.
     NOT min-width:auto: .hcol-with is white-space:nowrap, so the button's min-content is its FULL
     text and the byline would refuse to shrink at all, collapsing the author's name to 0 instead.
     Scoped to .ph-name at (0,2,0) so the legacy .hcol-slot facepile below the head is untouched. */
  .ph-name .hcol-in{min-width:36px;}
  .post-head a.au-link:hover .n{text-decoration:underline;}
  .verified{color:var(--accent);flex:none;}
  .ph-sub{font-family:var(--ff-mono);font-size:12px;color:var(--muted);margin-top:2px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .ph-sub .h{color:var(--accent);}
  .ph-sub .sep{opacity:.5;margin:0 5px;}
  .ph-sub .on{color:var(--green);}
  .follow-inline{margin-left:6px;color:var(--accent);font-weight:700;font-family:var(--ff-mono);font-size:12px;}
  .post-menu{
    margin-left:auto;flex:none;width:32px;height:32px;border-radius:var(--radius-sm);
    display:grid;place-items:center;background:none;border:none;color:var(--muted);
    transition:background .12s,color .12s;
  }
  .post-menu:hover{background:var(--surface-mono);color:var(--ink);}

  /* vertical rhythm — the head/body/react/actions gaps were 11/13/10; settled onto 10/12/10 */
  .post-body{margin-top:10px;font-size:14px;color:#24292F;line-height:1.6;}
  .post-body p{margin:0 0 9px;}
  .post-body strong{font-weight:600;}
  .post-body code{
    font-family:var(--ff-mono);font-size:12.5px;background:var(--surface-mono);
    border:1px solid var(--border);border-radius:4px;padding:1px 5px;color:var(--accent);
  }

  .tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px;}
  .tag{
    font-family:var(--ff-mono);font-size:11.5px;font-weight:500;color:var(--accent);
    background:var(--surface-mono);border:1px solid var(--border);border-radius:999px;
    padding:3px 9px;transition:background .12s,border-color .12s;
  }
  .tag:hover{background:var(--accent-wash);border-color:#B6E3FF;}

  /* PR card block */
  .pr-block{
    margin-top:12px;border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;
  }
  .pr-bar{
    display:flex;align-items:center;gap:10px;flex-wrap:wrap;
    padding:10px 12px;background:var(--surface-mono);border-bottom:1px solid var(--border);
    font-family:var(--ff-mono);font-size:12px;
  }
  .pr-state{
    display:inline-flex;align-items:center;gap:5px;
    background:var(--purple);color:#fff;font-weight:600;
    padding:3px 8px;border-radius:999px;font-size:11px;
  }
  .repo-path{color:var(--muted);}
  .repo-path b{color:var(--ink);font-weight:600;}
  .branch{background:var(--accent-wash);color:var(--accent);border:1px solid #B6E3FF;border-radius:4px;padding:1px 6px;}
  .diff-add{color:var(--green);font-weight:700;}
  .diff-del{color:var(--danger);font-weight:700;}
  .pr-diffbar{margin-left:auto;display:flex;gap:2px;}
  .pr-diffbar span{width:9px;height:9px;border-radius:2px;}
  .sq-a{background:var(--green);} .sq-d{background:var(--danger);} .sq-n{background:var(--border-strong);}

  .code{
    position:relative;background:#0D1117;color:#C9D1D9;
    font-family:var(--ff-mono);font-size:12.5px;line-height:1.7;
    padding:14px 14px;overflow-x:auto;
  }
  .code pre{margin:0;}
  .code .cm{color:#8B949E;}
  .code .kw{color:#FF7B72;}
  .code .fn{color:#D2A8FF;}
  .code .st{color:#A5D6FF;}
  .code .ty{color:#79C0FF;}
  .copy-btn{
    position:absolute;top:8px;right:8px;
    display:inline-flex;align-items:center;gap:6px;
    font-family:var(--ff-mono);font-size:11px;font-weight:600;
    color:#C9D1D9;background:#21262D;border:1px solid #30363D;border-radius:5px;
    padding:4px 9px;transition:background .12s,border-color .12s,color .12s;
  }
  .copy-btn:hover{background:#30363D;border-color:#8B949E;}
  .copy-btn.copied{color:#3FB950;border-color:#238636;}

  /* Media */
  .media{
    margin-top:12px;border:1px solid var(--border);border-radius:var(--radius);
    overflow:hidden;background:var(--surface-mono);max-height:62vh;
  }
  .media svg{display:block;width:100%;height:auto;}
  .media img{display:block;width:100%;max-height:62vh;object-fit:cover;}
  .media video{display:block;width:100%;max-height:62vh;object-fit:contain;background:#000;}
  @media(max-width:680px){.media{max-height:52vh;}.media img,.media video{max-height:52vh;}}
  .media-cap{
    padding:8px 12px;font-family:var(--ff-mono);font-size:11.5px;color:var(--muted);
    border-top:1px solid var(--border);background:var(--surface-mono);
    display:flex;align-items:center;gap:8px;
  }

  /* Hiring / rich card */
  .rich{
    margin-top:12px;border:1px solid var(--border);border-radius:var(--radius);
    padding:14px;background:
      linear-gradient(180deg,#F6F8FA,#fff);
  }
  .rich-top{display:flex;align-items:center;gap:11px;}
  .rich-logo{
    width:44px;height:44px;border-radius:var(--radius-sm);flex:none;display:grid;place-items:center;
    background:#0D1117;color:#fff;font-weight:800;font-family:var(--ff-mono);
  }
  .rich h4{margin:0;font-size:15px;font-weight:700;letter-spacing:-0.01em;}
  .rich .role-sub{font-family:var(--ff-mono);font-size:12px;color:var(--muted);margin-top:2px;}
  .rich-meta{display:flex;flex-wrap:wrap;gap:6px;margin:12px 0;}
  .meta-chip{
    display:inline-flex;align-items:center;gap:5px;
    font-family:var(--ff-mono);font-size:11.5px;color:var(--muted);
    background:#fff;border:1px solid var(--border);border-radius:999px;padding:3px 9px;
  }
  .meta-chip svg{stroke-width:2;color:var(--faint);}
  .rich-actions{display:flex;gap:8px;flex-wrap:wrap;}

  /* Engagement */
  .react-row{
    display:flex;align-items:center;gap:8px;
    margin-top:12px;padding-top:2px;
    font-family:var(--ff-mono);font-size:12px;color:var(--muted);
  }
  .react-cluster{display:flex;align-items:center;}
  .react-cluster span{
    width:20px;height:20px;border-radius:50%;display:grid;place-items:center;
    font-size:11px;border:2px solid var(--card);margin-left:-6px;background:var(--surface-mono);
  }
  .react-cluster span:first-child{margin-left:0;}
  .rc-up{background:var(--accent-wash);}
  .rc-fire{background:var(--amber-bg);}
  .rc-rocket{background:var(--green-bg);}
  .react-row .rr-right{margin-left:auto;display:flex;gap:12px;}

  .actions{
    display:flex;gap:4px;margin-top:10px;padding-top:10px;border-top:1px solid var(--border);
  }
  .act{
    flex:1;display:inline-flex;align-items:center;justify-content:center;gap:7px;
    padding:8px 6px;border-radius:var(--radius-sm);border:none;background:none;
    color:var(--muted);font-size:12.5px;font-weight:500;
    transition:background .12s,color .12s;
  }
  .act svg{stroke-width:1.8;}
  .act:hover{background:var(--surface-mono);color:var(--ink);}
  .act .ct{font-family:var(--ff-mono);font-size:12px;}
  .act.upvote.on{color:var(--accent);}
  .act.upvote.on svg{fill:var(--accent-wash);}
  .act.comment:hover{color:var(--accent);}
  .act.repost:hover{color:var(--green);}
  /* React / Share states. home.blade.php carries an identical inline copy of these four rules;
     promoting them to the shared sheet is what gives explore / bookmarks / profile / community /
     post the same reacted state instead of leaving it to each blade. Keep the blade copy — the
     declarations are identical, so the duplication is inert. */
  .act.react:hover{color:var(--accent);}
  .act.react.on{color:var(--accent);}
  .act.react.on svg{fill:var(--accent-wash);}
  .act.share:hover{color:var(--green);}

  /* ---------- RIGHT RAIL ---------- */
  .rightrail{position:sticky;top:80px;display:flex;flex-direction:column;gap:16px;}
  .rr-card{padding:14px 16px;}
  .rr-title{
    display:flex;align-items:center;gap:8px;
    font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:var(--muted);
    margin-bottom:12px;
  }
  .rr-title a{margin-left:auto;font-family:var(--ff-mono);font-size:11px;color:var(--accent);text-transform:none;letter-spacing:0;font-weight:500;}

  .strength .lvl{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px;}
  .strength .lvl b{font-size:14px;}
  .strength .lvl .pct{font-family:var(--ff-mono);font-size:13px;color:var(--green);font-weight:700;}
  .bar{height:6px;border-radius:999px;background:var(--surface-mono);border:1px solid var(--border);overflow:hidden;}
  .bar i{display:block;height:100%;width:82%;background:linear-gradient(90deg,var(--accent),var(--green));border-radius:999px;}
  .strength .hint{margin:10px 0 0;font-size:12px;color:var(--muted);line-height:1.45;}
  .strength .hint a{color:var(--accent);font-weight:600;}

  .pymk{display:flex;flex-direction:column;gap:2px;}
  .pymk-row{display:flex;align-items:center;gap:11px;padding:8px 0;border-bottom:1px solid var(--border);}
  .pymk-row:last-child{border-bottom:none;}
  .pymk-main{min-width:0;flex:1;}
  .pymk-main .n{font-weight:600;font-size:13px;line-height:1.2;display:inline-flex;align-items:center;gap:5px;}
  .pymk-main .r{font-family:var(--ff-mono);font-size:11px;color:var(--muted);margin-top:2px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .pymk-main .mut{color:var(--faint);}

  .trend{display:flex;flex-direction:column;}
  .trend-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--border);}
  .trend-row:last-child{border-bottom:none;}
  .trend-rank{font-family:var(--ff-mono);font-size:13px;font-weight:700;color:var(--faint);width:18px;flex:none;text-align:center;}
  .trend-main{min-width:0;flex:1;}
  .trend-main .t{font-family:var(--ff-mono);font-size:13px;font-weight:600;color:var(--ink);}
  .trend-main .t .h{color:var(--accent);}
  .trend-main .s{font-size:11.5px;color:var(--muted);margin-top:1px;}
  .trend-up{font-family:var(--ff-mono);font-size:11px;color:var(--green);font-weight:600;flex:none;}
  .star-btn{flex:none;width:30px;height:30px;border-radius:var(--radius-sm);display:grid;place-items:center;background:none;border:1px solid var(--border);color:var(--muted);transition:.12s;}
  .star-btn:hover{color:var(--amber);border-color:var(--amber);background:var(--amber-bg);}

  .rr-footer{padding:2px 4px 0;}
  .rr-links{display:flex;flex-wrap:wrap;gap:x;gap:4px 12px;font-family:var(--ff-mono);font-size:11.5px;}
  .rr-links a{color:var(--muted);}
  .rr-links a:hover{color:var(--accent);}
  .rr-copy{margin-top:10px;font-family:var(--ff-mono);font-size:11px;color:var(--faint);}
  .rr-copy .g{color:var(--green);}

  /* ---------- SCRIM ---------- */
  .scrim{
    position:fixed;inset:0;background:rgba(31,35,40,0.42);z-index:55;
    opacity:0;visibility:hidden;transition:opacity .12s,visibility .12s;
  }
  .scrim.show{opacity:1;visibility:visible;}

  /* ---------- RESPONSIVE ---------- */
  @media(max-width:1024px){
    .layout{grid-template-columns:220px minmax(0,1fr);}
    .rightrail{display:none;}
    .topnav-item .tlabel{display:none;}
    .topnav-item{min-width:44px;}
  }
  @media(max-width:820px){
    .search{max-width:none;}
    /* hudr.net: the sidebar becomes position:fixed at 820px (see ~line 1072) and so leaves grid
       flow, and .rightrail is already display:none from 1024px. The track count MUST collapse at
       this SAME breakpoint — otherwise .feed is the only in-flow grid item and auto-places into
       the orphaned 220px track declared at line 521, squeezing the whole feed (and the comment
       reply input down to ~29px) across the entire 681-820px band. The drawer styles below used
       to start at 680px, which also left the sidebar untranslated and painting over the feed
       while .sidebar-close was visible but useless. shell.js:769 already treats <=820 as drawer
       mode, so this aligns CSS with the behaviour JS was already assuming. */
    .layout{grid-template-columns:minmax(0,1fr);}
    .hamburger{display:grid;place-items:center;width:40px;height:40px;flex:none;
      border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--card);color:var(--ink);}
    .sidebar{
      position:fixed;top:0;left:0;bottom:0;width:284px;max-width:86vw;z-index:65;
      background:var(--canvas);border-right:1px solid var(--border);
      padding:16px 14px calc(20px + env(safe-area-inset-bottom));
      overflow-y:auto;transform:translateX(-102%);
      transition:transform .16s ease;box-shadow:var(--shadow-pop);
      border-radius:0;
    }
    .sidebar.open{transform:translateX(0);}
  }
  @media(max-width:680px){
    .topbar-inner{gap:10px;padding:0 14px;}
    /* .hamburger / .sidebar / .sidebar.open now live in the 820px block above — they must apply
       across the whole drawer range, not just below 680px. */
    .logo span.word{display:none;}
    .kbd{display:none;}
    .search input{padding-right:14px;}
    .topnav .t-hidephone{display:none;}
    .topnav-item{min-width:40px;height:40px;}

    .layout{gap:16px;padding:16px 14px calc(80px + env(safe-area-inset-bottom));}
    .act .ct{display:inline;}
    /* touch targets — compact post action buttons (React / Comment / Share) on mobile */
    .nav-item{padding:12px 10px;}
    .actions{gap:2px;}
    .act{padding:7px 4px;min-height:36px;font-size:12px;gap:5px;}
    .act svg{width:17px;height:17px;}
    /* the ⋯ is the only tap target on the head row — match .act's 36px on phones */
    .post-menu{width:36px;height:36px;}
    .tab{padding:9px 12px;}
    .chip-action{padding:9px 11px;}
  }
  @media(max-width:430px){
    .ph-sub .role{display:none;}
    /* …and take its leading separator with it, or the sub-row reads "@handle · · 2h".
       If :has() is unsupported the rule is simply dropped and the row is exactly as it is today. */
    .ph-sub .sep:has(+ .role){display:none;}
    .composer-actions .chip-action .clabel{display:none;}
    .rich-actions .btn{flex:1;}
  }

  @media (prefers-reduced-motion: reduce){
    *{transition:none !important;animation:none !important;scroll-behavior:auto !important;}
  }

/* ===================== hudr demo additions ===================== */
.util-btn{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:9999px;color:var(--muted);background:none;border:none;cursor:pointer;transition:background .12s,color .12s;text-decoration:none}
.util-btn:hover{background:var(--surface-mono);color:var(--accent)}
.sidebar-utils{display:flex;justify-content:space-around;align-items:center;padding:6px}
/* rich emoji picker (comments + messenger) — glass, categorized, scrollable */
.emoji-picker{bottom:calc(100% + 8px);left:0;width:328px;max-width:calc(100vw - 24px);
  background:rgba(255,255,255,.82);-webkit-backdrop-filter:saturate(180%) blur(18px);backdrop-filter:saturate(180%) blur(18px);
  border:1px solid rgba(255,255,255,.55);border-radius:14px;box-shadow:0 16px 40px rgba(31,35,40,.22);overflow:hidden}
:root[data-theme=dark] .emoji-picker{background:rgba(24,29,36,.78);border-color:rgba(255,255,255,.1);box-shadow:0 16px 40px rgba(1,4,9,.65)}
.ep-tabs{display:flex;gap:2px;padding:8px 8px 6px;border-bottom:1px solid var(--border)}
.ep-tab{flex:1;height:32px;border:none;background:none;border-radius:var(--radius-sm);color:var(--muted);display:grid;place-items:center;cursor:pointer;transition:.12s}
.ep-tab:hover{background:var(--surface-mono);color:var(--ink)}
.ep-tab.active{background:var(--accent-wash);color:var(--accent)}
.ep-body{max-height:264px;overflow-y:auto;padding:4px 8px 10px;scrollbar-width:thin}
.ep-body::-webkit-scrollbar{width:8px}.ep-body::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:8px;border:2px solid transparent;background-clip:content-box}
.ep-h{position:sticky;top:0;background:linear-gradient(var(--card),var(--card));font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);padding:8px 2px 5px;z-index:1}
:root[data-theme=dark] .ep-h{background:transparent}
.ep-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:1px}
.ep-grid button{font-size:22px;line-height:1;padding:5px 0;border:none;background:none;border-radius:var(--radius-sm);cursor:pointer;transition:transform .1s,background .12s}
.ep-grid button:hover{background:var(--surface-mono);transform:scale(1.22)}
@media(max-width:560px){
  /* position NEEDS !important: .nx-pop{position:absolute} is declared later in this same file at
     equal specificity (0,1,0) and would otherwise win. The offsets below already carry !important,
     so without it the sheet keeps position:absolute — and since the picker is mounted on <body>,
     that resolves against the document origin, parking it thousands of px above the viewport. */
  /* hudr.net: bottom:8px parked the sheet ON TOP of .mobile-tabbar (62px border-box, position:fixed
     bottom:0, shown on every page at <=820px) — and .emoji-picker's z-index 520 beats the bar's 95,
     so while the picker was open every tap aimed at Home/Explore/Create/Chat/Profile landed on the
     emoji grid and the bottom nav was simply unreachable. 70px = the 62px bar + the same 8px gap. */
  .emoji-picker{position:fixed!important;left:8px!important;right:8px!important;bottom:70px!important;top:auto!important;width:auto;max-width:none}
  .ep-body{max-height:44vh}
  .ep-grid{grid-template-columns:repeat(8,1fr)}
}
/* create-post composer tool chips (Photo/Video/GIF/Poll/Audio/Record/Emoji) */
.composer-tools{display:flex;gap:6px;margin-top:12px;flex-wrap:wrap}
.composer-tool{display:inline-flex;align-items:center;gap:7px;padding:8px 12px;border:1px solid var(--border);border-radius:999px;background:var(--card);color:var(--muted);font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;transition:.12s}
.composer-tool span{color:var(--ink)}
.composer-tool:hover{border-color:var(--accent);background:var(--surface-mono)}
.composer-tool svg{flex:none}
.composer-tool.t-photo svg{color:var(--green)} .composer-tool.t-video svg{color:var(--accent)}
.composer-tool.t-gif svg{color:var(--purple)} .composer-tool.t-poll svg{color:var(--amber)}
.composer-tool.t-audio svg{color:#EC6EAD} .composer-tool.t-rec svg{color:var(--danger)}
.composer-tool.t-emoji svg{color:var(--amber)}
@media(max-width:520px){.composer-tool span{display:none}.composer-tool{padding:9px}}
/* composer attachment area (media preview + poll builder) */
.composer-attach:empty{display:none}
.composer-attach{margin-top:12px}
.cx-media{display:flex;align-items:center;gap:12px;padding:12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-mono)}
.cx-media-ic{width:44px;height:44px;border-radius:var(--radius-sm);display:grid;place-items:center;background:var(--card);border:1px solid var(--border);font-size:20px;font-family:var(--ff-mono);font-weight:700;color:var(--accent);flex:none}
.cx-media-b{flex:1;min-width:0}
.cx-media-t{font-weight:600;font-size:13.5px;color:var(--ink)}
.cx-media-s{font-size:12px;color:var(--muted);margin-top:2px}
.cx-media-x{width:30px;height:30px;border-radius:50%;border:none;background:none;color:var(--muted);display:grid;place-items:center;cursor:pointer;flex:none}
.cx-media-x:hover{background:var(--card);color:var(--danger)}
.cx-poll{border:1px solid var(--border);border-radius:var(--radius);padding:12px;background:var(--surface-mono);display:flex;flex-direction:column;gap:8px}
.cx-poll-h{font-weight:700;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.cx-poll-o{width:100%;border:1px solid var(--border);border-radius:var(--radius-sm);padding:9px 11px;font:inherit;font-size:14px;color:var(--ink);background:var(--card);outline:none}
.cx-poll-o:focus{border-color:var(--accent)}
.cx-poll-add{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:2px}
.cx-poll-addbtn{border:1px dashed var(--border-strong);background:none;color:var(--accent);font:inherit;font-size:12.5px;font-weight:600;padding:7px 12px;border-radius:999px;cursor:pointer}
.cx-poll-addbtn:hover{background:var(--accent-wash)}
.cx-poll-dur{border:1px solid var(--border);border-radius:var(--radius-sm);padding:7px 9px;font:inherit;font-size:12.5px;color:var(--ink);background:var(--card);cursor:pointer}
.cx-poll-x{margin-left:auto;border:none;background:none;color:var(--muted);font:inherit;font-size:12.5px;font-weight:600;cursor:pointer}
.cx-poll-x:hover{color:var(--danger)}
/* GIF picker (Giphy) — overlays the create-post card; tokens auto-adapt to dark theme */
.cx-gifpicker{position:absolute;inset:0;z-index:5;display:flex;flex-direction:column;background:var(--card);border-radius:10px;overflow:hidden}
.cx-gifpicker[hidden]{display:none}
.nx-modal-card:has(.cx-gifpicker:not([hidden])){min-height:min(560px,82vh)}
.cx-gif-head{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--border);flex:none}
.cx-gif-back{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:1px solid var(--border);border-radius:8px;background:var(--card);color:var(--ink);cursor:pointer;flex:none;transition:.12s}
.cx-gif-back:hover{border-color:var(--accent);color:var(--accent)}
.cx-gif-search{flex:1;display:flex;align-items:center;gap:8px;padding:8px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface-mono);color:var(--muted)}
.cx-gif-search svg{flex:none}
.cx-gif-search input{flex:1;border:none;background:none;outline:none;font:inherit;font-size:14px;color:var(--ink)}
/* real vertical grid — flows top→bottom and scrolls DOWN (column-count overflowed sideways) */
.cx-gif-grid{flex:1;overflow-y:auto;overflow-x:hidden;padding:10px 14px;display:grid;grid-template-columns:repeat(3,1fr);gap:6px;align-content:start;min-height:180px}
.cx-gif-cell{position:relative;width:100%;aspect-ratio:1/1;padding:0;border:none;border-radius:6px;overflow:hidden;cursor:pointer;background:var(--surface-mono);transition:box-shadow .12s}
.cx-gif-cell:hover{box-shadow:0 0 0 2px var(--accent)}
.cx-gif-cell img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.cx-gif-busy{opacity:.5;pointer-events:none}
.cx-gif-msg{grid-column:1/-1;text-align:center;color:var(--muted);font-size:13px;padding:30px 0}
.cx-gif-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:11px;font-weight:600;letter-spacing:.03em;color:var(--muted);text-align:right;flex:none}
@media(max-width:520px){.cx-gif-grid{grid-template-columns:repeat(2,1fr)}}
/* mini-modal (language picker, account switcher) */
.hudr-mini{position:fixed;inset:0;z-index:360;display:flex;align-items:center;justify-content:center;padding:16px}
.hudr-mini-bd{position:absolute;inset:0;background:rgba(20,24,31,.5)}
:root[data-theme=dark] .hudr-mini-bd{background:rgba(1,4,9,.72)}
.hudr-mini-card{position:relative;width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow-pop);max-height:82vh;display:flex;flex-direction:column;animation:nxPop .16s ease}
.hudr-mini-h{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--border);flex:none}
.hudr-mini-h h3{margin:0;font-size:15px;font-weight:700}
.hudr-mini-x{width:32px;height:32px;border:none;background:none;color:var(--muted);border-radius:8px;display:grid;place-items:center;cursor:pointer}
.hudr-mini-x:hover{background:var(--surface-mono);color:var(--ink)}
.hudr-mini-b{padding:10px;overflow-y:auto}
.hudr-mini-load{padding:26px;text-align:center;color:var(--muted);font-size:13px}

/* reusable confirm dialog (window.hudrConfirm) — destructive-action guard, theme-aware.
   z-index 500 keeps it above the media viewer (200), mini-modal (360) and lightbox (400). */
.hudr-confirm{position:fixed;inset:0;z-index:500;display:flex;align-items:center;justify-content:center;padding:16px}
.hudr-confirm-bd{position:absolute;inset:0;background:rgba(20,24,31,.5);animation:hudrCfFade .12s ease}
:root[data-theme=dark] .hudr-confirm-bd{background:rgba(1,4,9,.72)}
.hudr-confirm-card{position:relative;width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow-pop);padding:22px 22px 18px;text-align:center;animation:nxPop .16s ease}
.hudr-confirm-ico{width:46px;height:46px;margin:0 auto 12px;border-radius:50%;display:grid;place-items:center;background:var(--accent-wash);color:var(--accent)}
.hudr-confirm.is-danger .hudr-confirm-ico{background:rgba(207,34,46,.1);color:var(--danger)}
:root[data-theme=dark] .hudr-confirm.is-danger .hudr-confirm-ico{background:rgba(248,81,73,.14)}
.hudr-confirm-t{margin:0 0 6px;font-size:16px;font-weight:700;color:var(--ink)}
.hudr-confirm-m{margin:0;font-size:13.5px;line-height:1.5;color:var(--muted)}
.hudr-confirm-foot{display:flex;gap:9px;margin-top:18px}
.hudr-confirm-foot .btn{flex:1;justify-content:center}
.hudr-confirm.closing{opacity:0;transition:opacity .12s ease}
.btn-danger{background:var(--danger);color:#fff;border-color:var(--danger)}
.btn-danger:hover{filter:brightness(.94)}
@keyframes hudrCfFade{from{opacity:0}to{opacity:1}}

/* ---------- post-publish share popup (hudr.net) ---------- */
.hudr-sharepop{padding:2px 4px}
.hudr-sp-lead{margin:0 0 14px;font-size:13.5px;color:var(--muted);line-height:1.5}
.hudr-sp-copy{display:flex;gap:8px;align-items:center;margin-bottom:14px}
.hudr-sp-copy input{flex:1;min-width:0;height:38px;padding:0 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface-mono);color:var(--ink);font-size:13px;text-overflow:ellipsis}
.hudr-sp-copybtn{flex:none;height:38px;padding:0 14px;display:inline-flex;align-items:center;gap:6px;border:none;border-radius:8px;background:var(--accent);color:#fff;font-size:13px;font-weight:600;cursor:pointer;transition:background .12s}
.hudr-sp-copybtn:hover{background:var(--accent-hover)}
.hudr-sp-native{width:100%;height:40px;margin:0 0 12px;display:inline-flex;align-items:center;justify-content:center;gap:8px;border:1px solid var(--border);border-radius:8px;background:var(--card);color:var(--ink);font-size:13.5px;font-weight:600;cursor:pointer;transition:background .12s}
.hudr-sp-native:hover{background:var(--surface-mono)}
.hudr-sp-nets{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px}
.hudr-sp-net{display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 4px;border:1px solid var(--border);border-radius:10px;background:var(--card);color:var(--ink);font-size:11.5px;font-weight:500;cursor:pointer;transition:background .12s,border-color .12s}
.hudr-sp-net:hover{background:var(--surface-mono);border-color:var(--accent)}
.hudr-sp-ic{display:grid;place-items:center;color:var(--muted);transition:color .12s}
.hudr-sp-net:hover .hudr-sp-ic{color:var(--accent)}
.hudr-sp-foot{display:flex;justify-content:flex-end;gap:8px;border-top:1px solid var(--border);padding-top:12px;margin-top:2px}
.hudr-lang-row{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;padding:11px 12px;border:1px solid transparent;border-radius:8px;background:none;color:var(--ink);font:inherit;font-size:14px;font-weight:500;cursor:pointer;text-align:left}
.hudr-lang-row:hover{background:var(--surface-mono)}
.hudr-lang-row.is-current{color:var(--accent);font-weight:600}
.hudr-lang-row svg{color:var(--accent);flex:none}
.hudr-lang-row:disabled{opacity:.5;pointer-events:none}
.hudr-acc-row{display:flex;align-items:center;gap:11px;width:100%;padding:10px 12px;border:1px solid transparent;border-radius:10px;background:none;color:var(--ink);font:inherit;cursor:pointer;text-align:left}
.hudr-acc-row:hover:not(.is-active){background:var(--surface-mono)}
.hudr-acc-row.is-active{cursor:default}
.hudr-acc-row:disabled{opacity:.55;pointer-events:none}
.hudr-acc-main{flex:1;min-width:0}
.hudr-acc-nm{display:block;font-size:14px;font-weight:600}
.hudr-acc-hd{display:block;font-size:12px;color:var(--muted);font-family:var(--ff-mono)}
.hudr-acc-cur{font-size:11px;font-weight:600;color:var(--accent);background:var(--accent-wash);border-radius:999px;padding:2px 9px;flex:none}
:root[data-theme=dark] .hudr-acc-cur{background:rgba(31,111,235,.16)}
.hudr-acc-empty{color:var(--muted);font-size:13px;line-height:1.5;padding:6px 8px 14px;margin:0}
.hudr-acc-manage{width:100%;justify-content:center;text-decoration:none}
.inv-modal{display:flex;flex-direction:column;gap:11px;padding:4px}
.inv-desc{margin:0;color:var(--muted);font-size:13px;line-height:1.5}
.inv-ta{width:100%;border:1px solid var(--border);border-radius:8px;padding:10px 12px;font:inherit;font-size:14px;color:var(--ink);background:var(--card);outline:none;resize:vertical}
.inv-ta:focus{border-color:var(--accent)}
.inv-modal .btn-block{width:100%;justify-content:center}
/* stories: full-screen viewer */
.st-viewer{position:fixed;inset:0;z-index:300;background:rgba(6,8,12,.92);display:flex;align-items:center;justify-content:center;padding:14px}
/* a proper 9:16 story card that adapts to ANY screen: driven by available height (dvh handles mobile
   browser chrome), width derived from the aspect ratio, and capped so it never exceeds the viewport. */
.st-stage{position:relative;aspect-ratio:9/16;height:min(90dvh,820px);width:auto;max-width:96vw;background:#000;border-radius:14px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.6)}
.st-media{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#000}
.st-media img,.st-media video{width:100%;height:100%;object-fit:contain}
.st-bars{position:absolute;top:8px;left:8px;right:8px;z-index:4;display:flex;gap:4px}
.st-bar{flex:1;height:3px;background:rgba(255,255,255,.32);border-radius:2px;overflow:hidden}
.st-bar i{display:block;height:100%;width:0;background:#fff;border-radius:2px}
.st-bar.done i{width:100%}
.st-top{position:absolute;top:18px;left:10px;right:44px;z-index:4;display:flex;align-items:center;gap:9px;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.5)}
.st-top .avatar{border:2px solid rgba(255,255,255,.85)}
.st-who{display:flex;flex-direction:column;line-height:1.2;min-width:0}
.st-who b{font-size:13.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.st-who span{font-size:11.5px;opacity:.85}
.st-close{position:absolute;top:14px;right:10px;z-index:5;width:34px;height:34px;border:none;background:rgba(0,0,0,.25);color:#fff;border-radius:50%;display:grid;place-items:center;cursor:pointer}
.st-close:hover{background:rgba(0,0,0,.45)}
.st-cap{position:absolute;left:0;right:0;bottom:0;z-index:4;padding:34px 16px 96px;background:linear-gradient(to top,rgba(0,0,0,.62),transparent);pointer-events:none}
.st-cap-in{color:#fff;font-size:14px;line-height:1.4;text-shadow:0 1px 3px rgba(0,0,0,.6)}
.st-nav{position:absolute;top:0;bottom:0;width:38%;z-index:3;border:none;background:none;cursor:pointer}
.st-prev{left:0}.st-next{right:0;width:62%}
/* stories: editor / composer */
.st-editor{display:flex;flex-direction:column;gap:12px;padding:4px}
.st-ed-drop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;width:100%;min-height:180px;border:2px dashed var(--border-strong);border-radius:12px;background:var(--surface-mono);color:var(--muted);cursor:pointer;font:inherit}
.st-ed-drop:hover{border-color:var(--accent);color:var(--accent)}
.st-ed-drop.st-ed-busy{opacity:.6;pointer-events:none}
.st-ed-t{font-size:14px;font-weight:600;color:var(--ink)}
.st-ed-hint{font-size:12px;color:var(--muted)}
.st-ed-prev{width:100%;max-height:46vh;border-radius:12px;overflow:hidden;background:#000;display:flex;align-items:center;justify-content:center}
.st-ed-prev img,.st-ed-prev video{width:100%;max-height:46vh;object-fit:contain}
.st-ed-cap{width:100%;border:1px solid var(--border);border-radius:8px;padding:10px 12px;font:inherit;font-size:14px;color:var(--ink);background:var(--card);outline:none;resize:none}
.st-ed-cap:focus{border-color:var(--accent)}
.st-ed-share{width:100%;justify-content:center}
.st-more{position:absolute;top:14px;right:50px;z-index:5;width:34px;height:34px;border:none;background:rgba(0,0,0,.25);color:#fff;border-radius:50%;display:grid;place-items:center;cursor:pointer}
.st-more:hover{background:rgba(0,0,0,.45)}
.st-menu{position:absolute;top:52px;right:12px;z-index:6;min-width:150px;background:var(--card);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column}
.st-mi{border:none;background:none;color:var(--ink);font:inherit;font-size:13.5px;font-weight:500;text-align:left;padding:9px 10px;border-radius:7px;cursor:pointer}
.st-mi:hover{background:var(--surface-mono)}
.st-mi.danger{color:var(--danger)}
.hudr-report{display:flex;flex-direction:column;gap:2px}
.hudr-rep-desc{color:var(--muted);font-size:12.5px;line-height:1.45;padding:2px 8px 10px;margin:0}
.hudr-rep-row{display:flex;flex-direction:column;gap:2px;text-align:left;width:100%;padding:10px 12px;border:1px solid transparent;border-radius:8px;background:none;color:var(--ink);font:inherit;cursor:pointer}
.hudr-rep-row:hover{background:var(--surface-mono)}
.hudr-rep-row b{font-size:13.5px;font-weight:600}
.hudr-rep-row span{font-size:11.5px;color:var(--muted);line-height:1.4}
.hudr-rep-row:disabled{opacity:.5;pointer-events:none}
/* story footer: reactions + reply (viewer) / viewers count (owner) */
.st-foot{position:absolute;left:0;right:0;bottom:0;z-index:5;padding:10px 12px 14px;display:flex;flex-direction:column;gap:9px}
.st-react{display:flex;justify-content:center;gap:6px}
/* each reaction springs in (staggered) when the story loads; backwards fill avoids a pre-delay flicker
   and — unlike forwards — leaves :hover / selected transforms free to apply afterwards. */
.st-react-b{width:40px;height:40px;flex:none;border:none;background:rgba(0,0,0,.32);border-radius:50%;font-size:21px;line-height:1;cursor:pointer;display:grid;place-items:center;transform-origin:center bottom;transition:transform .12s,background .12s,box-shadow .12s;animation:stReactIn .5s cubic-bezier(.34,1.56,.64,1) backwards;animation-delay:calc(var(--i,0) * .05s)}
.st-react-b .rx-anim,.st-react-b img{width:27px;height:27px;object-fit:contain;pointer-events:none;display:block}
.st-react-b:hover{background:rgba(0,0,0,.5);transform:translateY(-3px) scale(1.2)}
.st-react-b.st-react-on{background:#fff;transform:scale(1.14);box-shadow:0 3px 14px rgba(0,0,0,.45)}
.st-react-b.st-react-pop{animation:stPop .5s cubic-bezier(.34,1.56,.64,1)}
@keyframes stReactIn{0%{opacity:0;transform:translateY(14px) scale(.2)}55%{opacity:1;transform:translateY(-6px) scale(1.3)}78%{transform:translateY(0) scale(.92)}100%{opacity:1;transform:none}}
@keyframes stPop{0%{transform:scale(1)}35%{transform:scale(1.65)}70%{transform:scale(.92)}100%{transform:scale(1.14)}}
@media(prefers-reduced-motion:reduce){.st-react-b{animation:none}}
.st-views{display:inline-flex;align-items:center;gap:7px;align-self:center;border:none;background:rgba(0,0,0,.32);color:#fff;font:inherit;font-size:13px;font-weight:600;padding:9px 18px;border-radius:999px;cursor:pointer}
.st-views:hover{background:rgba(0,0,0,.48)}
.st-vrow{display:flex;align-items:center;gap:11px;padding:9px 10px;border-radius:8px}
.st-vrow:hover{background:var(--surface-mono)}
.st-vmain{flex:1;min-width:0}
.st-vname{display:block;font-size:14px;font-weight:600;color:var(--ink)}
.st-vhd{display:block;font-size:12px;color:var(--muted);font-family:var(--ff-mono)}
.st-vreact{font-size:18px;flex:none;line-height:1}
.st-vtime{font-size:12px;color:var(--muted);flex:none}
/* story OWNER bar — the author's footer: viewers pill (+ compact reaction summary), stats, edit */
.st-obar{display:flex;align-items:center;justify-content:center;gap:8px}
.st-ob{display:inline-flex;align-items:center;gap:7px;border:none;background:rgba(0,0,0,.32);color:#fff;font:inherit;font-size:13px;font-weight:600;padding:9px 14px;border-radius:999px;cursor:pointer;flex:none;transition:background .12s}
.st-ob:hover{background:rgba(0,0,0,.48)}
.st-ob-rx{font-weight:500;opacity:.92;letter-spacing:.02em}
/* story OWNER sheet — slides up INSIDE the 9:16 stage so it reads as part of the story card, and is
   automatically full-width on phones where the stage is the whole screen. Scrim + panel; the panel
   is a flex column whose body is the only scroller (overscroll contained so the page never moves). */
.st-sheetwrap{position:absolute;inset:0;z-index:7;display:flex;align-items:flex-end}
.st-sheet-scrim{position:absolute;inset:0;background:rgba(0,0,0,.45);animation:stScrimIn .18s ease}
.st-sheet{position:relative;width:100%;max-height:76%;background:var(--card);border-radius:16px 16px 0 0;box-shadow:0 -12px 40px rgba(0,0,0,.45);display:flex;flex-direction:column;animation:stSheetIn .24s cubic-bezier(.32,.72,.35,1)}
@keyframes stScrimIn{from{opacity:0}}
@keyframes stSheetIn{from{transform:translateY(24px);opacity:.4}}
@media(prefers-reduced-motion:reduce){.st-sheet,.st-sheet-scrim{animation:none}}
.st-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--border-strong);margin:8px auto 2px;flex:none}
.st-sheet-h{display:flex;align-items:center;gap:10px;padding:6px 10px 8px 16px;flex:none}
.st-sheet-hm{flex:1;min-width:0}
.st-sheet-t{margin:0;font-size:15px;font-weight:700;color:var(--ink)}
.st-sheet-sub{display:block;font-size:12px;color:var(--muted);margin-top:1px}
.st-sheet-x{width:32px;height:32px;flex:none;border:none;border-radius:8px;background:none;color:var(--muted);cursor:pointer;display:grid;place-items:center}
.st-sheet-x:hover{background:var(--surface-mono);color:var(--ink)}
.st-sheet-b{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:0 8px 14px}
/* audience tabs: All + one chip per reaction actually present (with counts — the "overall" totals) */
.st-tabs{display:flex;gap:6px;padding:2px 8px 10px;overflow-x:auto;position:sticky;top:0;background:var(--card);z-index:1}
.st-tab{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--border);background:none;color:var(--ink);font:inherit;font-size:13px;padding:5px 12px;border-radius:999px;cursor:pointer;flex:none}
.st-tab b{font-weight:600;font-size:12px;color:var(--muted)}
.st-tab.on{background:var(--accent);border-color:var(--accent);color:#fff}
.st-tab.on b{color:rgba(255,255,255,.85)}
a.st-vrow{text-decoration:none;color:inherit}
/* owner statistics: stat tiles + single-hue meter rows — identity is carried by the text/emoji label
   and the count is a direct label in ink, so the bars need no legend and one accent hue suffices. */
.st-tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:2px 8px 10px}
.st-tile{background:var(--surface-mono);border:1px solid var(--border);border-radius:10px;padding:9px 11px;min-width:0}
.st-tile b{display:block;font-size:19px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;line-height:1.2}
.st-tile span{display:block;font-size:11px;color:var(--muted);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.st-sec{margin:8px 10px 4px;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted)}
.st-frow{display:flex;align-items:center;gap:10px;width:100%;padding:7px 10px;border:none;background:none;border-radius:8px;cursor:pointer;font:inherit;text-align:left}
.st-frow:hover{background:var(--surface-mono)}
.st-frow.now{background:var(--accent-wash)}
.st-fthumb{width:24px;height:36px;border-radius:6px;object-fit:cover;background:#000;flex:none}
.st-fnum{width:24px;height:36px;border-radius:6px;background:var(--surface-mono);border:1px solid var(--border);color:var(--muted);font-size:12px;font-weight:700;display:grid;place-items:center;flex:none}
.st-fr-t{flex:none;width:84px;min-width:0}
.st-fr-t b{display:block;font-size:12.5px;font-weight:600;color:var(--ink);white-space:nowrap}
.st-fr-t span{display:block;font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* meter track = var(--border): still visible when a row hover paints the row var(--surface-mono) */
.st-meter{flex:1;height:6px;border-radius:999px;background:var(--border);overflow:hidden;min-width:40px}
.st-meter i{display:block;height:100%;width:0;background:var(--accent);border-radius:999px}
.st-mr-n{flex:none;min-width:22px;text-align:right;font-size:12.5px;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
.st-rrow{display:flex;align-items:center;gap:10px;padding:6px 10px}
.st-rrow .st-re{flex:none;width:24px;font-size:17px;line-height:1;text-align:center}
.st-rrow .st-rl{flex:none;width:84px;font-size:12.5px;font-weight:600;color:var(--ink);text-transform:capitalize}
.st-note{margin:10px 12px 0;font-size:11.5px;color:var(--muted);line-height:1.45}
/* owner edit actions */
.st-ed-act{display:flex;align-items:center;gap:12px;width:100%;padding:11px 12px;border:none;background:none;border-radius:10px;cursor:pointer;font:inherit;color:var(--ink);text-align:left}
.st-ed-act:hover{background:var(--surface-mono)}
.st-ed-act svg{flex:none;color:var(--muted)}
.st-ed-act.danger,.st-ed-act.danger svg{color:var(--danger)}
.st-ed-act b{display:block;font-size:14px;font-weight:600}
.st-ed-act i{display:block;font-style:normal;font-size:11.5px;color:var(--muted);margin-top:1px}
.st-ed-note{margin:8px 12px 2px;font-size:11.5px;color:var(--muted);line-height:1.45}
/* stories: full-screen on phones (edge-to-edge, dynamic viewport so the footer never clips) */
@media(max-width:600px){
  .st-viewer{padding:0}
  .st-stage{aspect-ratio:auto;width:100%;max-width:100%;height:100vh;height:100dvh;border-radius:0}
  .st-foot{padding-bottom:calc(14px + env(safe-area-inset-bottom,0px))}
  /* the stage is the whole screen here, so give the owner sheet more headroom + the home-bar inset */
  .st-sheet{max-height:82dvh;padding-bottom:env(safe-area-inset-bottom,0px)}
}
.mono{font-family:var(--ff-mono)}
.btn.is-on{background:transparent}
.nx-modal{position:fixed;inset:0;z-index:200;display:flex;align-items:flex-start;justify-content:center;padding:6vh 16px;overflow:auto}
.nx-modal[hidden]{display:none}
.nx-modal-backdrop{position:absolute;inset:0;background:rgba(20,24,31,.5)}
.nx-modal-card{position:relative;width:100%;max-width:560px;background:var(--card);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow-pop);animation:nxPop .16s ease}
@keyframes nxPop{from{opacity:0;transform:translateY(-8px) scale(.99)}to{opacity:1;transform:none}}
.nx-modal-head{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid var(--border)}
.nx-modal-head h3{margin:0;font-size:16px;font-weight:700}
.nx-x{width:32px;height:32px;border-radius:var(--radius-sm);border:none;background:none;color:var(--muted);cursor:pointer;display:grid;place-items:center}
.nx-x:hover{background:var(--surface-mono);color:var(--ink)}
.nx-modal-body{padding:16px 18px}
.nx-modal-foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 18px;border-top:1px solid var(--border)}
.nx-toasts{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:9998;display:flex;flex-direction:column;gap:8px;align-items:center}
.nx-toast{background:var(--ink);color:#fff;font-size:13px;font-weight:500;padding:9px 16px;border-radius:8px;box-shadow:var(--shadow-pop);opacity:0;transform:translateY(8px);transition:opacity .22s,transform .22s}
.nx-toast.in{opacity:1;transform:none}
/* popover: use pointer-events (NOT visibility) so it is instantly clickable when .open —
   visibility:hidden was making the reactions picker un-hittable (clicks fell through to the avatar behind). */
/* the [hidden] attribute must always win over component display rules
   (fixes the reactions picker showing its full emoji grid → "huge" popup) */
[hidden]{display:none!important}
.nx-pop{opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .13s,transform .13s;position:absolute;z-index:120}
/* The emoji picker is mounted on <body> (shell.js), so elevateCard() can no longer find a
   .card/.post ancestor to raise — it silently no-ops for this panel. Give the picker its own
   stacking level instead, above cards and the sticky topbar. */
.emoji-picker{z-index:520}
.nx-pop.open{opacity:1;pointer-events:auto;transform:none}
.dropdown{z-index:120}

/* sticky columns (DESKTOP ONLY) so the feed scrolls independently of the sidebars/rail.
   Scoped to min-width:1025px so it never overrides the mobile sidebar drawer (position:fixed <=820px). */
@media(min-width:1025px){
  .sidebar{position:sticky;top:72px;align-self:start;max-height:calc(100vh - 84px);overflow:visible}
  .rightrail{position:sticky;top:72px;align-self:start;max-height:calc(100vh - 84px);overflow-y:auto;scrollbar-width:none}
  .rightrail::-webkit-scrollbar{display:none}
}

/* notifications: filter tabs + sound toggle */
.nx-notif-tabs{display:flex;gap:2px;padding:8px 12px 4px;border-bottom:1px solid var(--border)}
/* padding 6px -> 10px: the tab was ~31px tall, and on a phone this panel is the ONLY notifications
   inbox (there is no Notifications slot in the mobile tab bar), so its controls carry the whole
   surface. 10px brings it to ~39px and the row to a comfortable thumb target. */
.nx-notif-tab{flex:1;padding:10px 8px;border:none;background:none;font:inherit;font-size:12.5px;font-weight:600;color:var(--muted);border-radius:var(--radius-sm);cursor:pointer;transition:.12s}
.nx-notif-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.nx-notif-tab:hover{background:var(--surface-mono);color:var(--ink)}
.nx-notif-tab.active{background:var(--accent-wash);color:var(--accent)}
.nx-notif-sound{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 14px;border-top:1px solid var(--border);font-size:12.5px;color:var(--muted)}
.nx-notif-sound .lbl{display:flex;align-items:center;gap:8px;color:var(--ink);font-weight:500}
.nx-switch{position:relative;width:38px;height:22px;border-radius:999px;background:var(--border-strong);border:none;cursor:pointer;transition:background .15s;flex:none}
.nx-switch::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:transform .15s}
.nx-switch.on{background:var(--green)}
.nx-switch.on::after{transform:translateX(16px)}
@media(prefers-reduced-motion:reduce){.nx-toast,.nx-pop,.nx-modal-card,.flurry b{transition:none;animation:none}}

/* ---- follow "flurry" burst (matches old hudr micro-animation) ---- */
.flurry{position:absolute;left:50%;top:50%;width:0;height:0;pointer-events:none;z-index:130}
.flurry b{position:absolute;left:0;top:0;width:7px;height:7px;border-radius:50%;opacity:0;
  transform:translate(-50%,-50%);animation:flurryPop .62s cubic-bezier(.2,.7,.3,1) forwards}
@keyframes flurryPop{
  0%{opacity:1;transform:translate(-50%,-50%) scale(.3)}
  70%{opacity:1}
  100%{opacity:0;transform:translate(calc(-50% + var(--fx)),calc(-50% + var(--fy))) scale(1)}
}
.btn.pulse{animation:btnPulse .34s ease}
@keyframes btnPulse{0%{transform:scale(1)}40%{transform:scale(.94)}100%{transform:scale(1)}}

/* ---- report modal reasons ---- */
/* REPORT REASONS. The title and its description were running together — "Hate speechAttacks based
   on identity…" — because .rt and .rd are both <span>, i.e. INLINE, so they share a line and the
   `margin-top:2px` on .rd was silently doing nothing (vertical margins do not apply to inline
   boxes). Making the wrapper a column and the two parts block-level is the whole fix; the rest is
   the breathing room the owner asked for on the boxes themselves. */
.rep-list{display:flex;flex-direction:column;gap:8px}
.rep-opt{display:flex;align-items:flex-start;gap:12px;padding:13px 14px;border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:.12s;background:var(--card)}
.rep-opt:hover{border-color:var(--border-strong);background:var(--surface-mono)}
.rep-opt.sel{border-color:var(--accent);background:var(--accent-wash)}
.rep-opt input{margin-top:3px;flex:none;accent-color:var(--accent)}
/* min-width:0 so a long description wraps inside the row instead of widening the modal. */
.rep-opt > span{display:flex;flex-direction:column;min-width:0;flex:1}
.rep-opt .rt{display:block;font-weight:600;font-size:13.5px;line-height:1.35;color:var(--ink)}
.rep-opt .rd{display:block;font-size:12px;color:var(--muted);margin-top:4px;line-height:1.5}

/* ---- quote composer embed ---- */
.quote-embed{margin-top:12px;border:1px solid var(--border);border-radius:var(--radius);padding:12px;background:var(--surface-mono)}
.quote-embed .qh{display:flex;align-items:center;gap:8px;font-size:13px}
.quote-embed .qh .avatar{width:26px;height:26px;font-size:11px}
.quote-embed .qh .qn{font-weight:600;color:var(--ink)}
.quote-embed .qh .qu{font-family:var(--ff-mono);font-size:11.5px;color:var(--muted)}
.quote-embed .qb{margin-top:8px;font-size:13px;color:var(--ink);line-height:1.55}

/* ---- notifications panel (opens from the Alerts / sidebar bell) ---- */
.nx-notif{position:fixed;z-index:250;width:380px;max-width:calc(100vw - 24px);max-height:min(560px,80vh);
  background:var(--card);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow-pop);
  display:flex;flex-direction:column;overflow:hidden;opacity:0;transform:translateY(8px) scale(.98);
  transition:opacity .15s,transform .15s;pointer-events:none}
.nx-notif.open{opacity:1;transform:none;pointer-events:auto}
.nx-notif-head{display:flex;align-items:center;justify-content:space-between;padding:13px 16px;border-bottom:1px solid var(--border)}
.nx-notif-head h4{margin:0;font-size:15px;font-weight:700}
/* was a bare 12px inline link ~18px tall. Padding + a hit box takes it to 32px and gives it a
   hover state, on a control that on phones is the only way to clear the whole list. */
.nx-notif-head a{font-size:12px;color:var(--accent);font-weight:600;text-decoration:none;
  padding:7px 9px;margin:-7px -9px;border-radius:7px}
.nx-notif-head a:hover{background:var(--accent-wash)}
.nx-notif-list{overflow-y:auto;padding:4px}
/* Day separators. The list had none — 300 rows in one string, with an hour ago and last March
   indistinguishable. Sticky so the bucket you are scrolling through stays named. */
.nx-notif-day{position:sticky;top:0;z-index:1;padding:9px 12px 5px;font-size:11px;font-weight:700;
  letter-spacing:.04em;text-transform:uppercase;color:var(--muted);
  background:linear-gradient(var(--card) 72%,transparent)}
.nx-notif-item{display:flex;gap:11px;align-items:flex-start;padding:10px 12px;border-radius:var(--radius);cursor:pointer;transition:background .12s;text-decoration:none;color:inherit}
.nx-notif-item:hover{background:var(--surface-mono)}
.nx-notif-item.unread{background:var(--accent-wash)}
.nx-notif-item .nb{font-size:13px;color:var(--ink);line-height:1.45}
.nx-notif-item .nb b{font-weight:600}
/* Was var(--ff-mono) 11px var(--faint). --faint (#8C959F) on --card is 3.27:1, and on an unread
   row's --accent-wash it is 2.69:1 — both under the 4.5:1 AA floor for text this size, and a
   monospace face is the least legible choice for the one string on the row that gets skimmed.
   --muted (#656D76) is 5.17:1 on card and 4.54:1 on accent-wash: passes on both backgrounds. */
.nx-notif-item .nt{font-size:11.5px;color:var(--muted);margin-top:3px}
/* The unread bar: a left rule as well as the wash, so unread is not carried by colour alone —
   the dot had no text alternative and the wash is invisible to a good number of readers. */
.nx-notif-item.unread{position:relative}
.nx-notif-item.unread::before{content:'';position:absolute;left:3px;top:9px;bottom:9px;width:3px;
  border-radius:2px;background:var(--accent)}
.nx-notif-item .ndot{width:8px;height:8px;border-radius:50%;background:var(--accent);flex:none;margin-top:6px}
.nx-notif-empty{padding:34px 16px;text-align:center;color:var(--muted);font-size:13px}
/* A LOAD FAILURE IS NOT AN EMPTY INBOX. Both used to render into .nx-notif-empty, identically —
   same size, same colour, same padding — so "we could not reach the server" and "you have nothing"
   were indistinguishable, and there was no way to retry. */
.nx-notif-error{padding:28px 18px;text-align:center;display:flex;flex-direction:column;
  align-items:center;gap:6px}
.nx-notif-error .ne-t{font-size:13.5px;font-weight:700;color:var(--ink)}
.nx-notif-error .ne-s{font-size:12.5px;color:var(--muted);max-width:30ch;line-height:1.5}
.nx-notif-error .btn{margin-top:6px}
.nx-notif-foot{padding:9px;border-top:1px solid var(--border);text-align:center}
.nx-notif-foot a{font-size:12.5px;color:var(--accent);font-weight:600;text-decoration:none}

/* ---- dark theme (toggle via data-theme=dark on <html>) ---- */
:root[data-theme=dark]{
  --canvas:#0D1117;--card:#161B22;--border:#30363D;--border-strong:#484f58;
  --ink:#E6EDF3;--muted:#9DA7B3;--faint:#6E7681;--accent:#58A6FF;--accent-hover:#79b8ff;
  --accent-wash:#132132;--green:#3FB950;--green-bg:#12261a;--purple:#BC8CFF;--amber:#D29922;--amber-bg:#2b230f;
  --danger:#F85149;--surface-mono:#21262D;--shadow:0 1px 0 rgba(1,4,9,.4);--shadow-pop:0 10px 30px rgba(1,4,9,.7);
  /* @hudr's avatar mark is one black glyph on transparency and the PAGE flips it, so its flip has
     to be a TOKEN like every colour above — not a second, separately-maintained copy of the
     "is it dark?" test. It is consumed once, at the foot of this file. See that block for why the
     flip cannot live inside the SVG. Adding a new way to go dark here (a system-preference block,
     a per-container theme scope) makes the mark follow automatically. */
  --mark-flip:invert(1);
  /* @hudr's owl is TWO-TONE, so it cannot be flipped with a filter the way a single-colour glyph
     can — inverting it turns the eye-whites black and the pupils white. The brand already ships a
     purpose-drawn file per theme (they are byte-identical apart from --ink and --paper being
     swapped), so the dark theme swaps the FILE instead. Declared here, beside --mark-flip, for the
     same reason: however the page decides it is dark, the owl decides the same way at the same
     moment. Consumed once, at the foot of this file. */
  --owl-src:url("/ux/brand/hudr-owl-avatar-dark.svg");
  /* @hudr's static "hudr" wordmark ships as a pair too, for a different reason than the owl: it IS
     single-colour, but it is drawn in the brand ink (#1d1f21) rather than pure #000, and
     invert(1) turns that into #e2e0de instead of the #f2efe7 paper every other dark surface uses.
     A purpose-drawn file per theme keeps the colour exact. Declared here so it flips off the same
     "is it dark?" test as --mark-flip and --owl-src; consumed once, at the foot of this file. */
  --wordmark-src:url("/ux/brand/hudr-wordmark-dark.svg");
}
:root[data-theme=dark] body{background:var(--canvas)}
:root[data-theme=dark] .post-body,
:root[data-theme=dark] .post-body p{color:#C9D1D9}
:root[data-theme=dark] .nx-modal-backdrop{background:rgba(1,4,9,.72)}
/* surfaces that were hardcoded white/light in light mode */
:root[data-theme=dark] .media{background:#0d1117}
:root[data-theme=dark] .rich{background:linear-gradient(180deg,#1b2027,#161b22)}
:root[data-theme=dark] .meta-chip{background:#0d1117}
:root[data-theme=dark] .tag:hover{border-color:#264a6b}
:root[data-theme=dark] .branch{border-color:#264a6b}
:root[data-theme=dark] .nx-toast{background:#2a3038;color:#E6EDF3;border:1px solid var(--border)}
/* button contrast in dark */
:root[data-theme=dark] .btn-outline{border-color:var(--border-strong);color:var(--ink);background:transparent}
:root[data-theme=dark] .btn-outline:hover{background:var(--surface-mono);border-color:var(--accent);color:var(--accent)}
:root[data-theme=dark] .btn-ghost{background:transparent;color:var(--ink);border-color:var(--border-strong)}
:root[data-theme=dark] .btn-ghost:hover{background:var(--surface-mono);border-color:var(--accent);color:var(--accent)}
:root[data-theme=dark] .btn-green{color:#fff}
/* any button that ended up white-on-white in dark: force readable */
:root[data-theme=dark] .btn:not(.btn-primary):not(.btn-green){color:var(--ink)}
:root[data-theme=dark] .btn:not(.btn-primary):not(.btn-green) svg{color:var(--ink)}

/* reactions picker: emojis pop in one-by-one (like the old hudr like-bar) */
/* glassmorphism reactions bar + bigger emojis (frosted-glass like real hudr) */
.rx-pop.open{background:rgba(255,255,255,.66);-webkit-backdrop-filter:saturate(180%) blur(16px);backdrop-filter:saturate(180%) blur(16px);border:1px solid rgba(255,255,255,.55);box-shadow:0 12px 34px rgba(31,35,40,.20)}
:root[data-theme=dark] .rx-pop.open{background:rgba(28,33,40,.62);border-color:rgba(255,255,255,.12);box-shadow:0 12px 34px rgba(1,4,9,.6)}
.rx-pop{padding:5px 7px}
.rx-pop .rx-row{gap:2px}
.rx-pop .rx-row > button{width:38px;height:38px;font-size:23px;border-radius:50%;transition:transform .12s ease}
.rx-pop .rx-row > button:hover{transform:scale(1.25)}
.rx-pop .rx-row > button.rx-more{font-size:18px}
/* hudr.net: your CURRENT reaction, shown selected inside the picker. Tapping it re-sends the same
   unified_id, which ReactionService treats as "remove" — and that is now the ONLY un-react path,
   because the per-type .rx pills you used to tap to un-react are gone (replaced by the flat .rsum
   summary at the bottom of this file). Specificity (0,4,1) deliberately beats the six blades'
   inline `.rx-row button{background:none}` (0,2,1) whatever order the sheets land in. */
.rx-pop .rx-row > button.on{background:var(--accent-wash);box-shadow:inset 0 0 0 1.5px var(--accent)}
@media(max-width:560px){.rx-pop .rx-row > button{width:34px;height:34px;font-size:21px}}
/* base visible (opacity:1) + backwards fill → hidden only during the pre-delay, and :hover scaling
   still works after the pop (forwards fill used to lock transform and kill hover). */
/* REAL animated reactions — each emoji is an actual animated WebP (Google Noto Animated Emoji), so it
   genuinely comes alive (thumbs-up, heart beat, laugh, etc.) rather than a CSS shake. Lazy-loaded on
   first open (see hudrPopover) so the 512px assets never touch page load. */
.rx-anim{width:30px;height:30px;object-fit:contain;display:block;pointer-events:none;-webkit-user-drag:none;user-select:none}
.rx-pop .rx-row > button{opacity:1}
.rx-pop .rx-row > button:hover{transform:scale(1.3);z-index:2}
@media(max-width:560px){.rx-anim{width:27px;height:27px}}
/* comments: the box slides open, and each comment rises + fades in (staggered via --i on append) */
.cbox:not([hidden]){animation:cboxIn .26s cubic-bezier(.2,.8,.3,1)}
.cmt{animation:cmtIn .4s cubic-bezier(.2,.8,.3,1) backwards;animation-delay:calc(var(--i,0) * .06s)}
@keyframes cboxIn{0%{opacity:0;transform:translateY(-6px)}100%{opacity:1;transform:none}}
@keyframes cmtIn{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.cbox:not([hidden]),.cmt{animation:none}}
:root[data-theme=dark] input,
:root[data-theme=dark] textarea,
:root[data-theme=dark] .search input{color:var(--ink)}
:root[data-theme=dark] .search{background:var(--surface-mono);border-color:var(--border)}
:root[data-theme=dark] .kbd{background:#161b22;border-color:var(--border);color:var(--muted)}
:root[data-theme=dark] .topbar{background:rgba(13,17,23,.85);border-color:var(--border)}
:root[data-theme=dark] ::placeholder{color:var(--faint)}

/* ---------- create-post modal (real hudr-style) ---------- */
.cp-top{display:flex;gap:11px;align-items:center;margin-bottom:12px}
.cp-id{display:flex;flex-direction:column;gap:5px;min-width:0}
.cp-name{font-weight:700;font-size:14.5px;color:var(--ink);line-height:1}
.cp-privacy{display:inline-flex;align-items:center;gap:5px;align-self:flex-start;font:inherit;font-size:12px;font-weight:600;color:var(--muted);background:var(--surface-mono);border:1px solid var(--border);border-radius:999px;padding:3px 9px 3px 8px;cursor:pointer;transition:.12s}
.cp-privacy:hover{border-color:var(--accent);color:var(--accent)}
.cp-privacy svg:first-child{color:var(--accent)}
.cp-editor{position:relative}
.cp-editor textarea{width:100%;border:1px solid var(--border);border-radius:var(--radius);padding:12px 46px 12px 12px;font:inherit;font-size:16px;color:var(--ink);background:var(--card);outline:none;resize:vertical;min-height:96px;line-height:1.5}
.cp-editor textarea:focus{border-color:var(--accent)}
.cp-emoji{position:absolute;top:8px;right:8px;width:32px;height:32px;border:none;background:none;color:var(--muted);border-radius:50%;display:grid;place-items:center;cursor:pointer;transition:transform .12s,background .12s,color .12s}
.cp-emoji:hover{background:var(--surface-mono);color:var(--amber);transform:scale(1.08)}
.cp-note{display:flex;align-items:center;gap:7px;margin-top:14px;font-size:12px;color:var(--muted)}
.cp-note svg{color:var(--accent);flex:none}

/* create-post mode switch: "Write a post" vs "Add to media" (media-only upload) */
.cp-modeseg{display:flex;gap:4px;padding:4px;margin:0 0 14px;background:var(--surface-mono);border:1px solid var(--border);border-radius:999px}
.cp-mode{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:8px 10px;border:none;border-radius:999px;background:none;color:var(--muted);font:inherit;font-size:13px;font-weight:600;cursor:pointer;transition:.14s}
.cp-mode svg{flex:none;opacity:.85}
.cp-mode:hover{color:var(--ink)}
.cp-mode.on{background:var(--card);color:var(--accent);box-shadow:0 1px 2px rgba(16,24,40,.08)}
.cp-mode.on svg{opacity:1}
.cp-mode:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-wash)}
.cp-note.is-media svg{color:var(--green)}

/* ---------- mobile app-style bottom tab bar (shown <= 820px) ---------- */
/* header appearance toggle — moon in light mode, sun in dark; shown on mobile (desktop uses the sidebar) */
.hdr-theme{display:none}
.hdr-theme .ic-sun{display:none}
.hdr-theme .ic-moon{display:inline-block}
:root[data-theme=dark] .hdr-theme .ic-moon{display:none}
:root[data-theme=dark] .hdr-theme .ic-sun{display:inline-block}
.mobile-tabbar{display:none}
@media(max-width:820px){
  .hdr-theme{display:grid;place-items:center}
  /* hudr.net — mobile install banner. Sits directly above .mobile-tabbar (62px + safe area) so it
     never covers the tabs, and z-index 96 puts it over the bar's 95. Shown only by JS, and only to
     Android without a prior dismissal — see shell.js. */
  .hudr-install{
    position:fixed;left:8px;right:8px;z-index:96;
    bottom:calc(62px + env(safe-area-inset-bottom) + 8px);
    display:flex;align-items:center;gap:11px;
    padding:11px 12px;border-radius:14px;
    background:var(--card);border:1px solid var(--border);
    box-shadow:0 10px 30px rgba(31,35,40,.20);
    animation:hudrInstallUp .22s ease both;
  }
  :root[data-theme=dark] .hudr-install{background:#161b22;border-color:#2b333d;box-shadow:0 10px 30px rgba(1,4,9,.6)}
  @keyframes hudrInstallUp{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
  @media(prefers-reduced-motion:reduce){.hudr-install{animation:none}}
  .hudr-install img{width:40px;height:40px;border-radius:10px;flex:none}
  .hudr-install .txt{min-width:0;flex:1}
  .hudr-install .t{font-size:13.5px;font-weight:700;color:var(--ink);line-height:1.25}
  .hudr-install .s{font-size:11.5px;color:var(--muted);line-height:1.3;margin-top:1px}
  .hudr-install .go{
    flex:none;padding:8px 15px;border-radius:999px;border:none;cursor:pointer;
    background:var(--accent);color:#fff;font-size:13px;font-weight:700;text-decoration:none;
  }
  .hudr-install .x{
    flex:none;width:28px;height:28px;display:grid;place-items:center;
    background:none;border:none;cursor:pointer;color:var(--muted);border-radius:8px;
  }
  .hudr-install .x:hover{background:var(--surface-mono)}

  .mobile-tabbar{display:grid;grid-template-columns:repeat(5,1fr);align-items:center;
    position:fixed;bottom:0;left:0;right:0;height:62px;background:var(--card);
    border-top:1px solid var(--border);z-index:95;padding-bottom:env(safe-area-inset-bottom);
    box-shadow:0 -4px 22px rgba(31,35,40,.11)}
  :root[data-theme=dark] .mobile-tabbar{background:#161b22;border-top-color:#2b333d;box-shadow:0 -4px 22px rgba(1,4,9,.62)}
  .mtab{display:grid;place-items:center;height:62px;color:var(--muted);text-decoration:none;background:none;border:none;cursor:pointer;position:relative;-webkit-tap-highlight-color:transparent;transition:transform .12s,color .14s}
  .mtab-ic{position:relative;display:grid;place-items:center;width:54px;height:34px;border-radius:17px;transition:background .18s}
  .mtab svg{stroke-width:2}
  .mtab:not(.mtab-create):active{transform:scale(.9)}
  .mtab.active{color:var(--accent)}
  .mtab.active .mtab-ic{background:var(--accent-wash)}
  :root[data-theme=dark] .mtab.active .mtab-ic{background:rgba(31,111,235,.2)}
  /* prominent filled create button */
  .mtab-create{color:#fff}
  .mtab-plus{width:48px;height:37px;border-radius:15px;display:grid;place-items:center;
    background:linear-gradient(135deg,var(--accent),#2C7BE5);color:#fff;
    box-shadow:0 6px 16px rgba(9,105,218,.45);border:1px solid rgba(255,255,255,.24);transition:transform .12s}
  .mtab-create:active .mtab-plus{transform:scale(.92)}
  .mtab-create svg{stroke-width:2.7}
  .mtab-badge{position:absolute;top:-3px;right:1px;background:var(--danger);color:#fff;font-size:9.5px;font-weight:700;min-width:16px;height:16px;border-radius:8px;display:grid;place-items:center;padding:0 3px;border:2px solid var(--card);line-height:1}
  :root[data-theme=dark] .mtab-badge{border-color:#161b22}
  .mtab-avatar .avatar{width:27px;height:27px;font-size:11px;border:1.5px solid var(--border-strong)}
  .mtab-avatar.active .mtab-ic{background:transparent}
  .mtab-avatar.active .avatar{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
  /* app-style: primary destinations live in the bottom bar, so hide the top quick-links */
  .topnav > a.topnav-item{display:none}
  /* Install stays hidden up here on mobile, on purpose. The .tlabel is display:none at this width,
     so the header item rendered as a bare triangle with no word next to it — recognisable to nobody.
     Mobile gets the install banner below (.ix-install) instead, which has room to say what it is. */
  /* keep the top bell + theme + avatar clearly tappable */
  #notifBtn{border:1px solid var(--border);border-radius:10px}
}
/* ---- mobile: keep dropdowns/popovers on-screen (no overflow, comfy tap targets) ---- */
@media(max-width:560px){
  .dropdown{width:min(280px,calc(100vw - 24px))!important;max-height:70vh;overflow:auto}
  .dropdown .dd-item{padding:11px 10px}
  .share-menu{left:auto;right:0}
  .nx-modal{padding:0}
  .nx-modal-card{border-radius:14px 14px 0 0;max-width:none;width:100%;margin-top:auto;max-height:92vh;overflow:auto}
  .nx-notif{left:8px!important;right:8px!important;width:auto!important;top:64px!important;max-height:76vh}
  /* hudr.net: iOS Safari force-zooms the whole page whenever a focused input renders under 16px, and
     every comment/reply field ships 13-13.5px — .cbox-input on home/profile/explore/bookmarks/
     community + the profile media viewer, .cc-inwrap on /post, .cx-input on /reels. Tapping a
     comment box zoomed the layout and left it zoomed. Each page's own <style> is parsed AFTER this
     file, so an equal-specificity rule would lose the cascade; the [type=text]/element qualifier is
     what wins it. The inputs carry explicit heights, so nothing reflows. */
  .cbox-input input[type=text],.cc-inwrap input[type=text],input.cx-input{font-size:16px}
}

/* ============ real hudr branding: owl logo, boot loader, verified badge ============ */
/* owl logo mark (replaces the generic mark; swaps light/dark) */
.owl-mark{width:30px;height:30px;background:none!important;box-shadow:none!important;display:grid;place-items:center;padding:0;border-radius:0}
.owl-mark img{width:27px;height:27px;display:block}
.owl-mark .owl-d{display:none}
:root[data-theme=dark] .owl-mark .owl-l{display:none}
:root[data-theme=dark] .owl-mark .owl-d{display:block}

/* verified badge — the real solid check-verified-02 in brand blue */
.hudr-vbadge{display:inline-flex;align-items:center;justify-content:center;color:#0A66C2;flex:none;vertical-align:middle;margin-left:3px}
:root[data-theme=dark] .hudr-vbadge{color:#70B5F9}
.hudr-vbadge svg,.hudr-verified{width:100%;height:100%;display:block}
.verified{color:#0A66C2}
:root[data-theme=dark] .verified{color:#70B5F9}

/* breathing-owl full-screen boot loader */
/* smooth full-screen owl boot loader — soft radial bg (no heavy blur), GPU-accelerated breath, gentle fade */
.hudr-boot{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;
  background:var(--card) radial-gradient(closest-side at 50% 45%, rgba(9,105,218,.10), transparent 70%);
  opacity:1;transition:opacity .45s ease;will-change:opacity}
:root[data-theme=dark] .hudr-boot{background:#0d1117 radial-gradient(closest-side at 50% 45%, rgba(88,166,255,.12), transparent 70%)}
.hudr-boot.gone{opacity:0;pointer-events:none}
.hudr-owl-loader{width:72px;transform:translateZ(0);will-change:transform,opacity;animation:hudrOwlBreath 2s cubic-bezier(.4,0,.2,1) infinite;backface-visibility:hidden}
.hudr-owl-loader img{width:100%;display:block}
.hudr-owl-loader .owl-d{display:none}
:root[data-theme=dark] .hudr-owl-loader .owl-l{display:none}
:root[data-theme=dark] .hudr-owl-loader .owl-d{display:block}
@keyframes hudrOwlBreath{0%,100%{transform:scale(.94);opacity:.75}50%{transform:scale(1.02);opacity:1}}

/* inline 3-dot list/infinite-scroll loader (hudr-primary-animation) */
.hudr-primary-animation{--dp:rgba(120,120,120,.16);--ds:rgba(120,120,128,.26);width:8px;height:8px;border-radius:50%;display:inline-block;margin:14px 12px;animation:hudrDots 1s infinite linear alternate}
:root[data-theme=dark] .hudr-primary-animation{--dp:rgba(90,90,96,.5);--ds:rgba(120,120,128,.6)}
@keyframes hudrDots{0%{box-shadow:12px 0 var(--dp),-12px 0 var(--ds);background:var(--dp)}33%{box-shadow:12px 0 var(--dp),-12px 0 var(--ds);background:var(--ds)}66%{box-shadow:12px 0 var(--ds),-12px 0 var(--dp);background:var(--ds)}100%{box-shadow:12px 0 var(--ds),-12px 0 var(--dp);background:var(--dp)}}
@media(prefers-reduced-motion:reduce){.hudr-owl-loader,.hudr-boot-glow,.hudr-primary-animation{animation:none}}

/* ===================== Wave 1: shared infra ===================== */
/* profile feed spacing (posts were touching because #postsList had no gap) */
#postsList{display:flex;flex-direction:column;gap:14px}
#postsList:empty{display:none}

/* broken media / avatar placeholder — never show a blank box */
.media-broken{min-height:120px;display:grid!important;place-items:center;background:var(--surface-mono);color:var(--faint)}
.media-broken::after{content:"Media unavailable";font-family:var(--ff-mono);font-size:12px}

/* mobile sidebar drawer: a visible close (X) button + keep the scrim clickable */
.sidebar-close{display:none}
@media(max-width:820px){
  .sidebar-close{display:inline-flex;align-items:center;justify-content:center;position:absolute;top:10px;right:10px;
    width:38px;height:38px;border-radius:10px;border:1px solid var(--border);background:var(--card);color:var(--ink);z-index:3;box-shadow:var(--shadow)}
  .sidebar{position:fixed}
}

/* shared site footer (real, openable links to every hudr page) */
.hudr-footer{max-width:1200px;margin:12px auto 0;padding:24px 20px calc(88px + env(safe-area-inset-bottom));
  border-top:1px solid var(--border);color:var(--muted);font-size:12.5px}
.hudr-footer .hf-cols{display:flex;flex-wrap:wrap;gap:24px 44px}
.hudr-footer .hf-col{display:flex;flex-direction:column;gap:8px;min-width:130px}
.hudr-footer .hf-h{font-weight:700;color:var(--ink);font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;margin-bottom:2px}
.hudr-footer a{color:var(--muted);transition:color .12s}
.hudr-footer a:hover{color:var(--accent)}
.hudr-footer .hf-bottom{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:22px;padding-top:16px;border-top:1px solid var(--border);font-family:var(--ff-mono);font-size:11.5px}
.hudr-footer .hf-brand{display:flex;align-items:center;gap:8px;font-weight:700;color:var(--ink)}
.hudr-footer .hf-brand img{width:20px;height:20px}
.hudr-footer .hf-brand .owl-d{display:none}
:root[data-theme=dark] .hudr-footer .hf-brand .owl-l{display:none}
:root[data-theme=dark] .hudr-footer .hf-brand .owl-d{display:inline-block}
.hudr-footer .hf-sp{margin-left:auto}
@media(max-width:820px){.hudr-footer{padding-bottom:calc(76px + env(safe-area-inset-bottom))}.hudr-footer .hf-sp{margin-left:0}}

/* guest empty-state: big responsive owl + message (e.g. "Log in to see people you may know") */
.guest-empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;padding:24px 14px}
.guest-empty img{width:76px;height:76px;max-width:45%;opacity:.92}
.guest-empty p{margin:0;font-size:13px;color:var(--muted);line-height:1.5}
.guest-empty a{color:var(--accent);font-weight:600}
/* generic owl light/dark swap wrapper (reusable) */
.owl-swap .owl-d{display:none}
:root[data-theme=dark] .owl-swap .owl-l{display:none}
:root[data-theme=dark] .owl-swap .owl-d{display:inline-block}

/* shared image lightbox (click a post image to view it full-size) */
.hudr-lightbox{position:fixed;inset:0;z-index:400;display:flex;align-items:center;justify-content:center;background:rgba(1,4,9,.86);padding:24px}
.hudr-lightbox[hidden]{display:none}
.hudr-lb-img{max-width:96vw;max-height:92vh;border-radius:8px;box-shadow:0 20px 60px rgba(0,0,0,.5);object-fit:contain}
.hudr-lb-x{position:fixed;top:16px;right:16px;width:44px;height:44px;border-radius:50%;border:none;background:rgba(255,255,255,.14);color:#fff;display:grid;place-items:center;cursor:pointer}
.hudr-lb-x:hover{background:rgba(255,255,255,.26)}

/* composer media previews (real uploaded image/video thumbnails) */
.cx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(94px,1fr));gap:8px;margin-top:12px}
.cx-thumb{position:relative;border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;aspect-ratio:1;background:var(--surface-mono)}
.cx-thumb img,.cx-thumb video{width:100%;height:100%;object-fit:cover;display:block}
.cx-thumb-x{position:absolute;top:4px;right:4px;width:24px;height:24px;border-radius:50%;border:none;background:rgba(0,0,0,.6);color:#fff;display:grid;place-items:center;cursor:pointer}
.cx-thumb-x:hover{background:rgba(0,0,0,.82)}
/* audio draft chip (uploaded file or recorded voice note) */
.cx-audio{position:relative;display:flex;align-items:center;gap:10px;border:1px solid var(--border);border-radius:var(--radius);padding:10px 40px 10px 12px;background:var(--surface-mono);margin-top:12px}
.cx-audio>svg{flex:none;color:#EC6EAD}
.cx-audio audio{flex:1;min-width:0;height:34px}
.cx-audio .cx-thumb-x{top:50%;transform:translateY(-50%)}
/* live recording bar */
.cx-recbar{display:flex;align-items:center;gap:10px;border:1px solid var(--danger);border-radius:var(--radius);padding:11px 12px;background:var(--accent-wash);margin-top:12px}
.cx-recdot{width:11px;height:11px;border-radius:50%;background:var(--danger);flex:none;animation:cxrecpulse 1s infinite}
@keyframes cxrecpulse{0%,100%{opacity:1}50%{opacity:.28}}
.cx-rectime{font-family:var(--ff-mono);font-weight:700;font-size:13px;color:var(--ink)}
.cx-recwave{flex:1;color:var(--muted);font-size:12.5px}
.cx-recstop{border:none;background:var(--danger);color:#fff;font:inherit;font-size:12.5px;font-weight:600;padding:7px 16px;border-radius:999px;cursor:pointer}
.cx-recstop:hover{filter:brightness(.94)}
/* video upload — live progress ring */
.cx-thumb.cx-uploading{display:grid;place-items:center;background:#0d1117}
.cx-up{position:relative;width:52px;height:52px;display:grid;place-items:center}
.cx-up-ring{width:52px;height:52px;transform:rotate(-90deg)}
.cx-up-bg{fill:none;stroke:rgba(255,255,255,.18);stroke-width:3}
.cx-up-fg{fill:none;stroke:var(--accent);stroke-width:3;stroke-linecap:round;transition:stroke-dashoffset .18s linear}
.cx-up-pct{position:absolute;font-family:var(--ff-mono);font-size:9.5px;font-weight:600;color:#fff;text-align:center;line-height:1.1}
/* "also add to my media" opt-in (default OFF) */
.cp-mediamark{display:flex;align-items:flex-start;gap:9px;margin-top:13px;cursor:pointer;user-select:none}
.cp-mediamark input{position:absolute;opacity:0;width:0;height:0}
.cp-mm-box{flex:none;width:18px;height:18px;margin-top:1px;border:1.5px solid var(--border-strong);border-radius:5px;display:grid;place-items:center;color:transparent;transition:background .14s,border-color .14s,color .14s}
.cp-mm-box svg{width:12px;height:12px;stroke-width:3}
.cp-mediamark input:checked+.cp-mm-box{background:var(--accent);border-color:var(--accent);color:#fff}
.cp-mediamark input:focus-visible+.cp-mm-box{box-shadow:0 0 0 3px var(--accent-wash)}
.cp-mm-txt{font-size:13px;color:var(--ink);line-height:1.45}
.cp-mm-txt em{font-style:normal;color:var(--muted)}

/* ===================== collaborative posts (hudr.net) =====================
   Everything here is namespaced `.collab-*` so it can only ever style markup a renderer
   opts into: a post with no collaborators emits none of these elements and is therefore
   byte-for-byte identical to today. As a second line of defence every container that a
   renderer might create before it knows whether it has anything to put inside is
   `:empty`-collapsed, so an empty wrapper still costs zero pixels and zero layout. */

/* ---- facepile: overlapping avatar stack + the "+5" overflow chip ---- */
/* hudr.net: size is parameterised through two custom properties instead of three copies of
   the rule, because the same stack has to serve the feed (22px), the dense reel/profile
   overlays (18px) and the single-post header (28px). Technique is .react-cluster's negative
   margin-left, but collab avatars are real photos so they also need the ring + object-fit.
   The ring is `var(--card)` (not white) so it disappears into the card in BOTH themes. */
.collab-stack{--cs:22px;--cs-lap:7px;display:inline-flex;align-items:center;flex:none;vertical-align:middle}
.collab-stack:empty{display:none}
.collab-stack.is-sm{--cs:18px;--cs-lap:6px}
.collab-stack.is-lg{--cs:28px;--cs-lap:9px}
/* the .avatar-qualified copy is NOT redundant: .avatar.sm/.md/.lg hard-set width/height at
   specificity (0,2,0), which beats a bare `.collab-stack > *` (0,1,0). Matching that
   specificity here — later in the file — is what lets a renderer reuse the existing
   .avatar markup inside the stack without the sizes fighting. */
.collab-stack > *,
.collab-stack > .avatar{width:var(--cs);height:var(--cs);flex:none;
  display:grid;place-items:center;overflow:hidden;border-radius:50%;
  font-weight:700;line-height:1;font-size:calc(var(--cs) * .42);letter-spacing:0;
  border:2px solid var(--card);margin-left:calc(var(--cs-lap) * -1);
  transition:transform .12s,border-color .12s}
/* hudr.net: the initials fallback MUST NOT be part of the rule above. That rule is deliberately
   specificity (0,2,0) so it can resize a reused `.avatar.sm`, but the gradient classes
   .a-blue/.a-green/… are only (0,1,0) — a plain `background` there silently flattened every
   coloured avatar in the stack to grey. `:where()` drops this to (0,0,0) so any colour class wins
   and an avatar with no class still gets a visible disc instead of a hole. */
:where(.collab-stack) > *{background:var(--surface-mono);color:#fff}
.collab-stack > *:first-child{margin-left:0}
.collab-stack > img,
.collab-stack > * > img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
.collab-stack > a:hover,
.collab-stack > button:hover{transform:translateY(-1px);z-index:2;position:relative}
.collab-more{width:auto;min-width:var(--cs);padding:0 6px;border-radius:999px;cursor:pointer;
  background:var(--surface-mono);color:var(--muted);border:2px solid var(--card);
  box-shadow:inset 0 0 0 1px var(--border);
  font-family:var(--ff-mono);font-size:calc(var(--cs) * .42);font-weight:700}
.collab-more:hover{background:var(--accent-wash);color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.collab-more:focus-visible{border-radius:999px}
/* over video/photo (reels, media viewer, embeds) the card-coloured ring vanishes — swap it for glass */
.collab-stack.on-media > *,
.collab-stack.on-media > .avatar{border-color:rgba(255,255,255,.85)}
.collab-stack.on-media .collab-more{background:rgba(0,0,0,.42);color:#fff;box-shadow:none}

/* ---- the "with Jane Doe and 4 others" byline that sits under the author ---- */
.collab-line{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0;
  margin-top:5px;font-size:12px;line-height:1.35;color:var(--muted)}
.collab-line:empty{display:none}
.collab-with{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.collab-with a,.collab-with b{color:var(--ink);font-weight:600}
.collab-with a:hover{color:var(--accent);text-decoration:underline}
.collab-line.on-media{color:rgba(255,255,255,.82)}
.collab-line.on-media .collab-with a,.collab-line.on-media .collab-with b{color:#fff}
/* status pill — amber while the post is held, green once every invite has been answered.
   Both palettes are tokens (--amber-bg / --green-bg are re-declared under data-theme=dark),
   so this needs no dark-mode override of its own. */
.collab-flag{display:inline-flex;align-items:center;gap:5px;flex:none;
  padding:2px 9px;border-radius:999px;background:var(--amber-bg);color:var(--amber);
  font-family:var(--ff-mono);font-size:10.5px;font-weight:700;letter-spacing:.02em}
.collab-flag svg{stroke-width:2.2;flex:none}
.collab-flag.is-live{background:var(--green-bg);color:var(--green)}

/* ---- collaborator list (mounted in window.hudrModal → .hudr-mini-b) ---- */
/* .hudr-mini-card caps at 400px, which truncates a name + headline + status pill + Remove.
   Widen it only when it is actually holding this list (same :has() trick .cx-gifpicker uses). */
.hudr-mini-card:has(.collab-list){max-width:460px}
.collab-list{display:flex;flex-direction:column;gap:2px}
.collab-row{display:flex;align-items:center;gap:11px;padding:9px 10px;border-radius:8px;
  color:inherit;text-decoration:none;transition:background .12s}
.collab-row:hover{background:var(--surface-mono)}
.collab-row .avatar{flex:none}
.collab-row-main{flex:1;min-width:0}
.collab-nm{display:flex;align-items:center;gap:5px;min-width:0;font-size:14px;font-weight:600;color:var(--ink)}
.collab-nm > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.collab-hd{display:block;font-family:var(--ff-mono);font-size:12px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.collab-sub{display:block;font-size:12px;color:var(--muted);line-height:1.4;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* label ∈ author | accepted | invited | no_longer_invited — a refusal and a timeout MUST look
   identical, so `is-gone` is deliberately one neutral grey pill with no reason attached. */
.collab-tag{flex:none;padding:2px 9px;border-radius:999px;
  font-family:var(--ff-mono);font-size:10.5px;font-weight:700;letter-spacing:.02em}
.collab-tag.is-author{color:var(--accent);background:var(--accent-wash)}
.collab-tag.is-accepted{color:var(--green);background:var(--green-bg)}
.collab-tag.is-invited{color:var(--amber);background:var(--amber-bg)}
.collab-tag.is-gone{color:var(--faint);background:var(--surface-mono)}
.collab-rm{flex:none;border:1px solid var(--border);border-radius:999px;background:none;
  color:var(--muted);font:inherit;font-size:12px;font-weight:600;padding:5px 11px;cursor:pointer;
  transition:color .12s,border-color .12s,background .12s}
.collab-rm:hover{color:var(--danger);border-color:var(--danger);background:var(--card)}
.collab-rm:disabled{opacity:.5;pointer-events:none}
.collab-state{padding:24px 16px;text-align:center;color:var(--muted);font-size:13px;line-height:1.5}

/* ---- invite panel (composer) ---- */
.collab-panel{margin-top:13px;padding:11px 12px;background:var(--surface-mono);
  border:1px solid var(--border);border-radius:var(--radius)}
.collab-panel-h{display:flex;align-items:center;gap:8px;
  font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.collab-panel-h svg{flex:none;color:var(--accent)}
/* the "3/10" counter. The cap is enforced server-side; this only ever reports it. */
.collab-count{margin-left:auto;flex:none;padding:1px 8px;border-radius:999px;
  background:var(--card);border:1px solid var(--border);
  font-family:var(--ff-mono);font-size:11.5px;font-weight:700;color:var(--muted)}
.collab-count.is-full{color:var(--amber);border-color:var(--amber);background:var(--amber-bg)}
.collab-hint{margin:7px 0 0;font-size:11.5px;line-height:1.45;color:var(--faint)}
.collab-search{display:flex;align-items:center;gap:8px;height:38px;margin-top:9px;padding:0 12px;
  background:var(--card);border:1px solid var(--border);border-radius:999px;color:var(--faint);
  transition:border-color .12s,box-shadow .12s}
.collab-search:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-wash)}
.collab-search svg{flex:none}
.collab-search input{flex:1;min-width:0;border:none;background:none;outline:none;
  font:inherit;font-size:13.5px;color:var(--ink)}
.collab-search.is-off{opacity:.55;pointer-events:none}
.collab-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.collab-chips:empty{display:none}
.collab-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;
  padding:3px 8px 3px 3px;border-radius:999px;
  background:var(--accent-wash);border:1px solid var(--accent);color:var(--accent);
  font-size:12px;font-weight:600}
.collab-chip img,.collab-chip .avatar{width:20px;height:20px;flex:none;border-radius:50%;object-fit:cover;font-size:9px}
.collab-chip b{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.collab-chip-x{flex:none;padding:0;border:none;background:none;color:inherit;cursor:pointer;
  display:grid;place-items:center;border-radius:50%;transition:opacity .12s}
.collab-chip-x:hover{opacity:.65}
.collab-chip-x:focus-visible{border-radius:50%}
.collab-results{max-height:236px;margin-top:8px;overflow-y:auto;scrollbar-width:thin;
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius-sm)}
.collab-results::-webkit-scrollbar{width:8px}
.collab-results::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:8px;border:2px solid transparent;background-clip:content-box}
.collab-opt{display:flex;align-items:center;gap:11px;width:100%;padding:9px 11px;
  background:none;border:none;border-bottom:1px solid var(--border);
  color:var(--ink);font:inherit;text-align:left;cursor:pointer;transition:background .12s}
.collab-opt:last-child{border-bottom:none}
.collab-opt:hover{background:var(--surface-mono)}
.collab-opt.is-picked{background:var(--accent-wash)}
.collab-opt:disabled{opacity:.45;cursor:not-allowed}
.collab-opt .avatar{flex:none}
.collab-opt-main{flex:1;min-width:0}
.collab-opt-nm{display:flex;align-items:center;gap:4px;min-width:0;
  font-size:13px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.collab-opt-hd{font-family:var(--ff-mono);font-size:11.5px;color:var(--accent);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.collab-opt-ck{flex:none;opacity:0;color:var(--accent);transition:opacity .12s}
.collab-opt.is-picked .collab-opt-ck{opacity:1}

/* ---- invite / hold banner (post page, author's "waiting on N", consent-stale notice) ---- */
.collab-banner{display:flex;align-items:flex-start;gap:11px;margin-top:12px;padding:12px;
  background:var(--surface-mono);border:1px solid var(--border);border-radius:var(--radius)}
.collab-banner.is-invite{background:var(--accent-wash);border-color:var(--accent)}
.collab-banner.is-waiting,.collab-banner.is-stale{background:var(--amber-bg);border-color:var(--amber)}
.collab-banner-ic{display:grid;place-items:center;flex:none;width:34px;height:34px;border-radius:50%;
  background:var(--card);border:1px solid var(--border);color:var(--accent)}
.collab-banner.is-waiting .collab-banner-ic,.collab-banner.is-stale .collab-banner-ic{color:var(--amber)}
.collab-banner-b{flex:1;min-width:0}
.collab-banner-t{font-size:13.5px;font-weight:700;color:var(--ink);line-height:1.35}
.collab-banner-m{margin:3px 0 0;font-size:12.5px;line-height:1.45;color:var(--muted)}
.collab-banner-acts{display:flex;flex-wrap:wrap;gap:8px;margin-top:11px}
.collab-banner.is-busy{opacity:.6;pointer-events:none}
.collab-expiry{display:inline-flex;align-items:center;gap:5px;margin-top:8px;
  font-family:var(--ff-mono);font-size:11.5px;color:var(--muted)}
.collab-expiry svg{flex:none;stroke-width:2}
.collab-expiry.is-soon{color:var(--danger);font-weight:700}
/* the share sheet must not hand the author a link nobody can open yet — this is the strip that
   replaces the copy field + network buttons while the post is still held. */
.collab-note{display:flex;align-items:flex-start;gap:9px;padding:11px 12px;border-radius:var(--radius);
  background:var(--amber-bg);color:var(--amber);font-size:12.5px;font-weight:600;line-height:1.45}
.collab-note svg{flex:none;stroke-width:2;margin-top:1px}

@media(max-width:520px){
  /* stacked, full-width accept/decline: two 50%-wide buttons wrap into a thumb-reachable column */
  .collab-banner-acts .btn{flex:1 1 auto;justify-content:center}
}
@media(max-width:560px){
  /* hudr.net: iOS Safari force-zooms the page whenever a FOCUSED input renders under 16px and
     never zooms back out. Same fix already applied to the comment fields further up this file —
     the picker input carries an explicit height, so bumping the font reflows nothing. */
  .collab-search input{font-size:16px}
  .collab-results{max-height:44vh}
}

/* ===================== reactions summary (hudr.net) =====================
   ONE flat summary — an overlapping stack of up to 3 reaction types + a single total — replaces the
   old one-pill-per-type row. Seven reaction types rendered seven bordered .rx pills that wrapped onto
   two or three lines and, because a pill carries button chrome (border + fill + 999px radius), the
   card ended up reading as two competing toolbars stacked on each other.
   .react-cluster (line ~471) is the design system's OWN engagement stack and is already live on the
   logged-out marketing panel (layouts/mpa/parts/auth-showcase.blade.php) — this only wires it to real
   data instead of inventing a second shape, which also ends the signed-out/signed-in inconsistency.
   Rule of the component: abbreviated in the card, exact in .rsum-pop and in the native title. */

/* ---------- collapsed summary ---------- */
/* min-height keeps the row from growing when a post's first reaction lands, so the feed never jumps. */
.react-row{flex-wrap:nowrap;position:relative;min-height:22px;}
/* hudr.net: all six ux blades (home/explore/bookmarks/profile/post/community) ship their own inline
   `.react-row{flex-wrap:wrap}` in a <style> block the browser parses AFTER this sheet at equal
   specificity (0,1,0) — so the blade copy wins and the nowrap above is silently discarded. Same class
   of cascade bug as the .emoji-picker note further up this file. Those inline lines are deleted in
   this change; this (0,2,0) guard is what stops ONE missed blade from putting the summary back on two
   lines for 22k live accounts. Delete it the day no blade ships its own .react-row rule. */
.react-row.react-row{flex-wrap:nowrap;}
.react-row .rr-right{flex:none;white-space:nowrap;}
.rsum{
  display:inline-flex;align-items:center;gap:7px;min-width:0;
  padding:0;border:none;background:none;border-radius:6px;text-align:left;
  font-family:var(--ff-mono);font-size:12px;line-height:1.6;color:var(--muted);
  cursor:pointer;transition:color .12s;
}
.rsum .react-cluster{flex:none;}
/* the showcase fills .react-cluster with literal text emoji, the feed fills it with the same PNG art
   the pills used — so this rule ADDS an img case and changes nothing the showcase renders. */
.react-cluster img{width:15px;height:15px;display:block;}
/* your own reaction: pinned first in the stack and ringed in accent, so you always have visible proof
   you reacted even on a post where your type is nowhere near the top 3 by count.
   z-index is load-bearing — later siblings paint on top in a negative-margin stack, so without it the
   NEXT chip's `2px solid var(--card)` border paints over the right edge of your ring. */
.react-cluster span.mine{
  position:relative;z-index:2;
  background:var(--accent-wash);box-shadow:0 0 0 1.5px var(--accent);
}
/* the copy is the ONLY element in the row allowed to shrink (see the 360px budget below). */
.rsum-txt{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.rsum-txt b{font-weight:700;color:var(--ink);}
/* hover changes COLOUR only — never a background, never a border. The summary reports and the .actions
   row below acts; giving the summary button chrome is exactly what made the card read as two widgets. */
.rsum:hover{color:var(--accent);}
.rsum:hover .rsum-txt b{color:var(--accent);}

/* ---------- per-type breakdown (tap the summary) ---------- */
/* Opens UPWARD (bottom:100%) so it can never cover React / Comment / Share. Counts here are exact —
   the card abbreviates, this does not. Rows are informational, NOT buttons: two react entry points
   with different visual languages is the clutter this change deletes. Positioned against
   .react-row{position:relative}; the left/bottom/top offsets beat .nx-pop{position:absolute...} purely
   on source order (equal specificity, declared later in this file). */
.rsum-pop{
  left:0;bottom:calc(100% + 6px);top:auto;
  width:214px;max-width:calc(100vw - 44px);
  background:var(--card);border:1px solid var(--border);border-radius:10px;
  box-shadow:var(--shadow-pop);padding:6px;
}
.rsum-pop h4{
  margin:0;padding:4px 8px 6px;
  font-family:var(--ff-mono);font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em;color:var(--muted);
}
.rsum-brk{display:flex;align-items:center;gap:9px;padding:5px 8px;border-radius:var(--radius-sm);}
.rsum-brk img{width:18px;height:18px;flex:none;}
.rsum-brk .n{flex:1;min-width:0;font-size:12.5px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.rsum-brk .c{flex:none;font-family:var(--ff-mono);font-size:12px;font-weight:700;color:var(--muted);}
.rsum-brk.mine{background:var(--accent-wash);}
.rsum-brk.mine .n,.rsum-brk.mine .c{color:var(--accent);}

/* the total bumps when your reaction lands, so the summary visibly ANSWERS the React button instead
   of sitting beside it (output acknowledging input). */
@keyframes rsumBump{0%{transform:scale(1)}40%{transform:scale(1.18)}100%{transform:scale(1)}}
.rsum-txt b.bump{display:inline-block;animation:rsumBump .22s ease-out;}
@media(prefers-reduced-motion:reduce){.rsum-txt b.bump{animation:none}}

/* ---------- the 360px one-line guarantee ---------- */
/* At 360px: .layout padding 14px x2 -> 332px card, .post padding 16px x2 -> 300px of content.
   Measured in Chromium at 360px: 3-chip stack 48px + gap 7px + .rr-right 43px = 98px fixed, leaving
   ~200px for the copy, and "You and 12.3k others" renders at 132px — one line, 68px spare. Dropping
   the word "views" is what buys the last ~44px; the eye icon still says what the number is.
   Anything longer ellipsises inside .rsum-txt rather than wrapping, so the row height is 22px flat in
   every state. Only the copy may shrink: .rr-right and .rsum .react-cluster are both flex:none. */
@media(max-width:430px){
  .views .vw{display:none;}
}
/* Dark mode needs ZERO overrides: every value above is an existing custom property. Verified against
   the :root[data-theme=dark] block — --surface-mono #21262D chips ringed in --card #161B22 keep the
   2px separation, and --accent #58A6FF on --accent-wash #132132 clears contrast. */

/* ============ profile badge row: the catalog glyph (.pf-badge / .ep-chip) ============
   config/badges.php stores each catalog entry's `icon` as the INNER markup of a 24x24 stroke icon,
   and ProfileResource + ProfessionalProfileController@badgeCatalog hand that same string to both
   surfaces that draw a badge: the header row (.pf-badge, 11px) and the Edit-profile picker
   (.ep-chip, 12px). One definition, two type sizes — so the sizing has to be RELATIVE, or the two
   rows disagree about how big "Founder" is on the same page. 1.18em puts the glyph at 12.98px
   beside an 11px label and 14.16px beside a 12px one, i.e. inside the 13-14px band config/badges.php
   asks every badge to match optically, and a third badge size added later inherits it for free.

   COLOUR comes from the badge, always. `stroke:currentColor` means the glyph takes whatever `color`
   the .c-green/.c-blue/... token set on its badge — including the inverted white of .ep-chip.on,
   where a hardcoded hue would be invisible. fill/stroke are declared HERE rather than left to the
   attributes on the emitted <svg> because CSS beats presentation attributes: this is the one place
   the glyph's paint is decided, so a new catalog entry needs a path and a colour token and nothing
   else. An entry whose `icon` is null emits no <svg> and renders as a plain label, by design.

   SCOPE is `> svg` on the badge itself, never `.pf-badges svg`. The "Open to work" badge (.avail)
   can sit in the very same identity block with its own hand-placed briefcase, and it is styled
   exclusively in the inline <style> of resources/views/ux/profile.blade.php — see the note at the
   foot of this file. A row-level selector here would reach into it from the wrong file.

   WHERE THIS LIVES, AND WHAT WOULD KILL IT. The badge's BOX (padding, gap, font, radius and the
   .c-* colour tokens) is in that same blade <style>, which the browser parses AFTER this sheet. That
   split is safe only while the blade defines no `.pf-badge svg` / `.ep-chip svg` of its own: an
   equal-specificity (0,1,1) copy there wins on source order and this block goes silently dead, the
   same cascade bug .react-row and .avail each carry a note about above. Glyph here, box there. */
.pf-badge > svg,.ep-chip > svg{
  width:1.18em;height:1.18em;flex:none;display:block;
  fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}

/* ===================== hudr REVIEWS (.hrv-* / .rvm-*) =====================
   hudr.net: reviews are an OPT-IN, profile-scoped surface. The renderer emits no
   .hrv-* node at all on a profile that has reviews switched off, so every rule in
   this block is dead CSS there — it cannot move a normal profile by 1px. Nothing
   below touches .profile-meta, .profile-headrow, .feed-tabs, .card or .tab, and
   this sheet no longer restyles ANY pre-existing selector: the open-to-work badge
   that used to be re-skinned at the foot of this file is owned solely by
   profile.blade.php's inline <style> now (one definition, no specificity war).

   Colour: ONE hue is on screen per surface (the aggregate tier). The per-star ramp
   is never rendered side by side, which is what keeps it off the traffic-light.

   COLOUR-BLIND / GREYSCALE LEGIBILITY — hue is never the only channel. Deuteranopes
   are ~8% of men and #D1242F (1*) and #1A7F37 (5*) are close in LUMINANCE (.150 vs
   .157 relative), i.e. near-identical in greyscale. So every surface that shows a
   rating carries at least two of these non-hue channels, and most carry three:
     1. FILL LENGTH  — the primary channel. .hrv-stars-fg is a hard-clipped width
        (20% per star) against a much lighter track; the boundary is a ~3:1+
        luminance step, readable with zero colour perception.
     2. A NUMERAL    — 4.1 / "out of 5" on the pill and hero, the count + percent on
        every breakdown row, the count inside every filter chip.
     3. A WORD       — .hrv-c-verdict (Poor/Below par/Fair/Great/Excellent) beside
        every card's star run, and the same five words in the composer picker.
     4. TEXT LABELS  — "5", "4", "3"... on the breakdown rows and the filter chips.
   Consequence for implementers: never ship a star run as the sole carrier of a
   rating. If a surface has stars and no numeral and no word, it is a bug.

   CONTRAST — the fill-vs-track ratio is the one that has to hold, because that is
   the boundary carrying channel 1. Measured (WCAG 2.x relative luminance):
     light, track #E6EAEF:  1*4.34  2*3.16  3*3.15  4*3.11  5*4.20
     dark,  track #39404A:  1*3.12  2*4.14  3*4.15  4*4.59  5*4.12
   Getting light tiers 2/3/4 over 3:1 is why they are DEEPER than a naive vivid
   ramp: #DE6B1B/#C08A00/#4E9F2E measure 2.79 / 2.53 / 2.75 against the same track
   and FAIL. Do not "brighten" them back.

   And be clear about what that does NOT buy. In greyscale the light ramp lands at
   relative luminance .150 / .225 / .226 / .230 / .157 — tiers 2, 3 and 4 are
   effectively identical to each other, and 1 and 5 are effectively identical to
   each other. That is not a defect to fix by respacing the ramp; any five-hue ramp
   pinned to a single track converges like this. It is precisely why channels 1-4
   above are mandatory rather than nice-to-have. A star run with no numeral and no
   word beside it is unreadable to a deuteranope no matter which five hues you pick.
   ========================================================================= */
:root{
  /* vivid ramp — star glyphs, bar fills, 1px card rules. Shapes only. */
  --hrv-1:#D1242F; --hrv-2:#D2620F; --hrv-3:#AD7900; --hrv-4:#46952A; --hrv-5:#1A7F37;
  /* deep ramp — TEXT, and solid fills that carry white text. All >=4.5:1 on --card. */
  --hrv-1s:#B31E28; --hrv-2s:#A85413; --hrv-3s:#8A6500; --hrv-4s:#3C7A22; --hrv-5s:#166930;
  --hrv-on-solid:#FFFFFF;
  /* washes — explicit rgba, NOT color-mix(): a few % of live traffic is on engines
     older than color-mix (Safari <16.2 / Chrome <111) and would get NO background. */
  --hrv-w1:rgba(209,36,47,.10);  --hrv-w2:rgba(210,98,15,.12);
  --hrv-w3:rgba(173,121,0,.13);  --hrv-w4:rgba(70,149,42,.13);
  --hrv-w5:rgba(26,127,55,.11);
  --hrv-b1:rgba(209,36,47,.30);  --hrv-b2:rgba(210,98,15,.32);
  --hrv-b3:rgba(173,121,0,.34);  --hrv-b4:rgba(70,149,42,.34);
  --hrv-b5:rgba(26,127,55,.30);
  --hrv-track:#E6EAEF;    /* PARTIAL-fill companion: tuned so the fill clears 3:1 */
  /* ALL-EMPTY track (tier 0 + the unrated composer picker). #E6EAEF on white is
     1.21:1 — five invisible stars on the "No reviews yet" pill. There is no fill to
     contrast against in that state, so this one is tuned for visibility instead. */
  --hrv-track-0:#C7CFD9;
}
:root[data-theme=dark]{
  --hrv-1:#F85149; --hrv-2:#F0883E; --hrv-3:#D29922; --hrv-4:#63C132; --hrv-5:#3FB950;
  /* dark flips the solid fill: a vivid block + near-black ink reads far better on
     #0D1117 than a muddy deep tone with white text. */
  --hrv-1s:#F85149; --hrv-2s:#F0883E; --hrv-3s:#D29922; --hrv-4s:#63C132; --hrv-5s:#3FB950;
  --hrv-on-solid:#0D1117;
  /* w1 is .12, not .16 like its neighbours: #F85149 text on a .16 wash measures
     4.28:1 and misses AA. At .12 it is 4.55:1. The wash is fainter as a result, but
     --hrv-b1 (.34) draws the edge, so the chip/pill still reads as a filled shape. */
  --hrv-w1:rgba(248,81,73,.12);  --hrv-w2:rgba(240,136,62,.16);
  --hrv-w3:rgba(210,153,34,.16); --hrv-w4:rgba(99,193,50,.16);
  --hrv-w5:rgba(63,185,80,.15);
  --hrv-b1:rgba(248,81,73,.34);  --hrv-b2:rgba(240,136,62,.34);
  --hrv-b3:rgba(210,153,34,.34); --hrv-b4:rgba(99,193,50,.34);
  --hrv-b5:rgba(63,185,80,.32);
  /* NOT --surface-mono (#21262D): the track sits ON --surface-mono in .hrv-tools and
     on the collapsed composer, and an identical value made empty stars vanish. */
  --hrv-track:#39404A;
  --hrv-track-0:#586069;
}
/* tier classes — put ONE of these on any .hrv-* container and every child (stars,
   bars, chips, hero block) picks up the right hue from --hrv-c.
   Tier = Math.max(1,Math.min(5,Math.round(avg))), 0 when count === 0. */
.hrv-t1{--hrv-c:var(--hrv-1);--hrv-cs:var(--hrv-1s);--hrv-cw:var(--hrv-w1);--hrv-cb:var(--hrv-b1)}
.hrv-t2{--hrv-c:var(--hrv-2);--hrv-cs:var(--hrv-2s);--hrv-cw:var(--hrv-w2);--hrv-cb:var(--hrv-b2)}
.hrv-t3{--hrv-c:var(--hrv-3);--hrv-cs:var(--hrv-3s);--hrv-cw:var(--hrv-w3);--hrv-cb:var(--hrv-b3)}
.hrv-t4{--hrv-c:var(--hrv-4);--hrv-cs:var(--hrv-4s);--hrv-cw:var(--hrv-w4);--hrv-cb:var(--hrv-b4)}
.hrv-t5{--hrv-c:var(--hrv-5);--hrv-cs:var(--hrv-5s);--hrv-cw:var(--hrv-w5);--hrv-cb:var(--hrv-b5)}
/* no rating yet — neutral, never green */
.hrv-t0{--hrv-c:var(--faint);--hrv-cs:var(--muted);--hrv-cw:var(--surface-mono);--hrv-cb:var(--border)}

/* ---------- the redacted reviewer's disc ---------- */
/* ReviewResource sends `avatar_url:null` for a reviewer whose handle is hidden — the avatar file is
   one live path per account, publicly published and globally unique, so it does not encode the
   account, it IS the account — and the comment beside that line says the client draws its own
   neutral initial circle instead. This is that circle. Put .hrv-anon on the .avatar element.

   NEUTRAL, not one of the seven .a-* gradients, and the reason is measured rather than aesthetic.
   The palette slot is `Math.abs(Number(u.id) || init.length) % 7` in both renderers, and a
   hidden-handle reviewer arrives with id null, so every one of them falls through to the
   initials branch — a slot keyed on how many initials the shortened name yields, i.e. one or two
   values for the whole site. Measured on /@priya_sharma: index 1 for all five hidden reviewers,
   five identical a-green discs. A hue that is constant carries nothing but still
   LOOKS like it carries something — and worse, the same person's disc would change colour between a
   card where their handle shows (id-seeded) and one where it does not, which makes the redaction
   itself the thing the colour reports. One flat disc says the only true thing: no profile behind it.

   Specificity is 0,2,0, so it beats .a-blue…a-teal (0,1,0) wherever the class lands — the renderer
   may emit it INSTEAD of the palette class or alongside it and both give the same disc. The
   `background` SHORTHAND is what clears the gradient: .a-* set background-image, and a
   background-color longhand here would leave the gradient painted over the top.

   Dark needs no override — all three values are theme tokens. Measured: the initial is 4.93:1 on the
   light disc (#656D76 on #F6F8FA) and 6.24:1 on the dark one (#9DA7B3 on #21262D). */
.avatar.hrv-anon{
  background:var(--surface-mono);
  color:var(--muted);
  /* an inset ring, NOT a border: .avatar is a fixed 32/40/52px grid box, so a real border would eat
     into it and shrink the letter's cell. The ring is what makes the disc exist at all in light mode
     — measured, --surface-mono on --card is 1.06:1 there and 1.14:1 in dark.
     Not a new grey circle either: --surface-mono + an inset hairline is exactly how .collab-more
     ("+3 more") is drawn a few hundred lines up. --border-strong rather than its --border because
     that one sits inside a 2px --card border in a stack and this one sits bare on the card. */
  box-shadow:inset 0 0 0 1px var(--border-strong);
}

/* ---------- the star primitive: one markup shape, three sizes ---------- */
/* Partial fill is a clipped overlay, so there are no half-star assets and no
   per-star maths in JS: --hrv-fill is (value/5)*100 to 1dp. Integer ratings land
   inside a GAP at every size (verified 12/15/21px), so 4 stars never shows a sliver
   of a 5th. .hrv-stars-fg is overflow:hidden on an absolutely-positioned box — no
   clip-path, so it degrades to "empty" rather than "solid" on ancient engines.
   A11y: the WRAPPER carries aria-label with the value; BOTH glyph layers are
   aria-hidden, so a screen reader never reads ten stars. */
.hrv-stars{position:relative;display:inline-flex;flex:none;line-height:0;vertical-align:middle}
.hrv-stars-bg,.hrv-stars-fg{display:flex;gap:var(--hrv-gap,2px)}
.hrv-stars-bg{color:var(--hrv-track)}
.hrv-stars-fg{position:absolute;inset:0;width:var(--hrv-fill,0%);overflow:hidden;color:var(--hrv-c,var(--hrv-3))}
.hrv-stars svg{flex:none;width:var(--hrv-sz,15px);height:var(--hrv-sz,15px);fill:currentColor;stroke:none;display:block}
.hrv-stars.is-sm{--hrv-sz:12px;--hrv-gap:1.5px}
.hrv-stars.is-md{--hrv-sz:15px;--hrv-gap:2px}
.hrv-stars.is-lg{--hrv-sz:21px;--hrv-gap:3px}
/* nothing filled → swap to the visibility-tuned track (see --hrv-track-0) */
.hrv-t0 .hrv-stars-bg,.hrv-stars.hrv-t0 .hrv-stars-bg{color:var(--hrv-track-0)}

/* ---------- 1. profile SUMMARY TAG ---------- */
/* Its OWN row between .profile-meta and .pf-badges — never inside .profile-meta.
   That container is flex-wrap with gap:8px 16px and already wraps to three lines at
   360px; a pill dropped into it lands in an unpredictable slot. Its own row makes
   the position deterministic at every width and keeps the open-to-work badge (a
   statement about the PERSON) visually separate from reviews (a statement about
   the BUSINESS). */
.hrv-sumrow{margin-top:12px;display:flex}
.hrv-sumrow:empty{display:none}
/* hudr.net — the summary reads as a CREDENTIAL, not a banner.
   It used to render the whole sentence at once: 15px stars + "3.8" + "out of 5" + "·" +
   "Reviews by 5 customers", ~300px of pill sitting under the name, and because it was
   flex-wrap:wrap it broke onto TWO LINES INSIDE ITSELF on a narrow profile — which is the
   one thing a rating chip must never do, since a wrapped chip reads as a broken component
   rather than a compact fact.
   Now: one line, never wraps, ~150px. The redundancy is what went, not the information —
   five stars already say "out of 5", so the words did no work; and the count says the same
   thing in three words that it said in five. Everything removed below is removed because it
   was restating something already on screen, not because space was tight. */
.hrv-sum{
  display:inline-flex;align-items:center;flex-wrap:nowrap;white-space:nowrap;gap:0 7px;
  max-width:100%;min-width:0;
  padding:4px 11px 4px 9px;border-radius:999px;
  background:var(--hrv-cw);border:1px solid var(--hrv-cb);
  color:var(--ink);font-size:12.5px;line-height:1.25;text-decoration:none;
  transition:background .12s,border-color .12s,box-shadow .12s;
}
/* Smaller than .is-md's 15px: beside a name, the stars are a glance-target, not the headline.
   Set on the container so the size follows the pill and the class in the markup is untouched. */
.hrv-sum .hrv-stars{--hrv-sz:13px;--hrv-gap:1.5px}
/* "out of 5" — the five stars next to it already state the scale, at every width. */
.hrv-sum .hrv-sum-of{display:none}
/* "Reviews by 5 customers" -> "5 reviews" at EVERY width, not just on phones. Both variants
   already ship in the DOM, so this is a display swap: no resize listener, no reflow, and the
   long copy stays available to anything that wants it. */
.hrv-sum .hrv-sum-long{display:none}
.hrv-sum .hrv-sum-short{display:inline}
.hrv-sum:hover{border-color:var(--hrv-c);box-shadow:0 1px 2px rgba(31,35,40,.06)}
:root[data-theme=dark] .hrv-sum:hover{box-shadow:0 1px 2px rgba(1,4,9,.5)}
.hrv-sum:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.hrv-sum-score{display:inline-flex;align-items:baseline;gap:5px;flex:none}
.hrv-sum-score b{font-family:var(--ff-mono);font-size:15px;font-weight:700;letter-spacing:-.01em;color:var(--hrv-cs)}
.hrv-sum-of{font-size:11.5px;color:var(--muted)}
.hrv-sum-sep{color:var(--faint);flex:none}
.hrv-sum-n{font-size:12.5px;font-weight:600;color:var(--muted);min-width:0;overflow-wrap:anywhere}
.hrv-sum:hover .hrv-sum-n{color:var(--ink)}
/* both copy variants ship in the DOM; CSS picks one. No resize listener, no reflow. */
.hrv-sum-short{display:none}
/* 0 reviews: neutral pill, dashed edge — reads as an invitation, not a score. */
.hrv-sum.is-empty{background:var(--surface-mono);border-style:dashed;border-color:var(--border-strong)}
.hrv-sum.is-empty .hrv-sum-n{color:var(--accent)}

/* ---------- 2. the REVIEWS tab ---------- */
/* Count pill on the tab button. .tab.active is inverted (--ink bg / --card text) and
   the two themes SWAP those tokens, so the translucent chip needs a per-theme value
   or it disappears on the light chip dark mode produces. */
.tab .tab-n{font-family:var(--ff-mono);font-size:10.5px;font-weight:700;color:var(--faint);
  background:var(--surface-mono);border-radius:999px;padding:1px 6px;margin-left:1px}
.tab.active .tab-n{background:rgba(255,255,255,.16);color:inherit}
:root[data-theme=dark] .tab.active .tab-n{background:rgba(1,4,9,.14)}

/* ---- the owner's "reviews are off" hint, inside .feed-tabs ---- */
/* Reviews OFF means OFF: setupReviews() returns before it builds a tab, a panel or a listener. That
   is right for every viewer and invisible to the one person who can change it, so the owner gets a
   hint in the slot where the tab would have been. It must NOT read as a tab — dashed edge, no active
   state, no role=tab — because a control that looks like a tab and opens settings is a broken tab.
   Dashed is this sheet's existing word for "this does not exist yet, add it" (.hrv-f-photo,
   .hrv-sum.is-empty); solid-on-hover is the same promotion .hrv-f-photo uses.
   flex:none + nowrap because .feed-tabs is an overflow-x:auto row: the hint scrolls away with the
   tabs rather than squeezing them, exactly like a real tab does.
   Give it this class ALONE, not .tab as well — .tab.active inverts to --ink/--card, and anything
   that ever toggled that class would repaint the hint as the selected tab.
   OWNER-ONLY IS THE RENDERER'S JOB, and there is deliberately no `.is-owner`/display:none gate in
   this sheet. A CSS gate does not remove the copy from the bytes — it only stops one engine painting
   it — and profile.blade.php's own note on the SSR projection says that document is PUBLIC and
   edge-cacheable, so viewer-varying copy baked into it can be served to a stranger. Build the node
   for the owner or do not build it. */
.hrv-tabhint{
  display:inline-flex;align-items:center;gap:6px;flex:none;white-space:nowrap;
  padding:6px 11px;border-radius:var(--radius-sm);
  border:1px dashed var(--border-strong);background:none;
  font-family:var(--ff-mono);font-size:12px;font-weight:500;color:var(--muted);
  cursor:pointer;transition:border-color .12s,color .12s,background .12s,border-style .12s;
}
.hrv-tabhint svg{width:13px;height:13px;flex:none;display:block;fill:none;stroke:currentColor;stroke-width:2}
.hrv-tabhint:hover{border-style:solid;border-color:var(--accent);color:var(--accent);background:var(--accent-wash)}
.hrv-tabhint:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* panel stack. Guarded with :not([hidden]) so this can never beat the blade's
   .tab-panel[hidden]{display:none} — an ID selector would otherwise force the
   panel visible on every profile. */
#panel-reviews:not([hidden]),.hrv-panel:not([hidden]){display:flex;flex-direction:column;gap:14px}
.hrv-list{display:flex;flex-direction:column;gap:12px}
.hrv-list:empty{display:none}
.hrv-more{display:flex;justify-content:center;padding:2px 0}

/* ---- 2.2 header block: hero score + breakdown ---- */
.hrv-head{padding:0;overflow:hidden}
.hrv-hero{display:grid;grid-template-columns:minmax(150px,208px) minmax(0,1fr);gap:22px;padding:20px}
/* The ONLY solid colour block in the system — one saturated rectangle per page.
   light: deep ramp + white (>=5.2:1 every tier). dark: vivid ramp + #0D1117 (>=5.6:1). */
.hrv-score{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:7px;
  padding:16px 12px;border-radius:10px;
  background:var(--hrv-cs);color:var(--hrv-on-solid);
}
.hrv-score-v{font-size:40px;font-weight:800;line-height:1;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.hrv-score-of{font-size:11.5px;font-weight:600;opacity:.8;margin-top:-3px}
.hrv-score-meta{display:flex;flex-direction:column;align-items:center;gap:5px}
/* the track would vanish against the fill — invert both layers inside the hero */
.hrv-score .hrv-stars-bg{color:rgba(255,255,255,.32)}
.hrv-score .hrv-stars-fg{color:var(--hrv-on-solid)}
:root[data-theme=dark] .hrv-score .hrv-stars-bg{color:rgba(13,17,23,.28)}
.hrv-score-n{font-size:12px;font-weight:600;opacity:.86;font-family:var(--ff-mono)}
/* NO REVIEWS YET — tier 0 must NOT paint the solid block.
   .hrv-score fills itself with var(--hrv-cs), and .hrv-t0 resolves that to --muted, so a profile
   with nothing to show rendered a large mid-grey rectangle where the score belongs. On the dark
   theme a light-grey slab with faint stars inside is indistinguishable from a skeleton that never
   loaded — which is exactly how it was reported ("it says loading"). Nothing was actually pending:
   the API answered `count: 0` correctly and the page had finished.
   The solid block is a SCORE, and tier 0 has no score, so it gets a quiet outlined panel of the
   same footprint instead. The star colours have to be restated because the rules above deliberately
   invert both layers for legibility ON the fill, and there is no fill here. */
.hrv-t0 .hrv-score{
  background:transparent;
  color:var(--muted);
  border:1px dashed var(--border-strong);
}
.hrv-t0 .hrv-score .hrv-stars-bg,
.hrv-t0 .hrv-score .hrv-stars-fg{color:var(--hrv-track-0)}
:root[data-theme=dark] .hrv-t0 .hrv-score .hrv-stars-bg,
:root[data-theme=dark] .hrv-t0 .hrv-score .hrv-stars-fg{color:var(--hrv-track-0)}

/* Breakdown bars — MONOCHROME by design. This is the one place all five hues could
   co-exist, and letting them would turn the component into a traffic light. The row
   LABEL carries the value, the bar carries the volume, the count and percent carry
   it again in text. Single most important decision in the colour system. */
.hrv-bars{display:flex;flex-direction:column;gap:7px;min-width:0;justify-content:center}
/* fixed 34/1fr/36/40 grid: label, count and percent columns never move between rows,
   so the bars form a clean vertical edge. minmax(0,1fr) on the track is the overflow
   guarantee — it shrinks to zero before it pushes anything.
   ⚠ THE COUNT COLUMN IS 36px, NOT 34px, AND THAT IS LOAD-BEARING. .hrv-bar-n is 12px
   700 --ff-mono with tabular-nums, i.e. a 7.2px advance per digit: a five-figure count
   ("12345" on a profile with >9,999 five-star reviews) measures ~36px. At 34px the
   glyphs spilled left over the bar track, because a grid ITEM overflows its column —
   the column does not grow and text-align:right pins the right edge. 36px is the
   floor, and the 40px percent column beside it is untouched. */
.hrv-bar-row{
  display:grid;grid-template-columns:34px minmax(0,1fr) 36px 40px;align-items:center;gap:10px;
  background:none;border:none;padding:3px 4px;margin:0 -4px;border-radius:var(--radius-sm);
  cursor:pointer;font:inherit;text-align:left;transition:background .12s;
}
.hrv-bar-row:hover{background:var(--surface-mono)}
.hrv-bar-row:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.hrv-bar-l{display:inline-flex;align-items:center;gap:3px;font-family:var(--ff-mono);font-size:12px;font-weight:700;color:var(--muted);flex:none}
.hrv-bar-s{width:11px;height:11px;fill:var(--hrv-c);stroke:none;flex:none}
.hrv-bar{position:relative;height:9px;border-radius:999px;background:var(--hrv-track);overflow:hidden;min-width:0}
.hrv-bar > i{display:block;height:100%;border-radius:999px;background:var(--hrv-c);
  transform-origin:left;animation:hrvBar .5s cubic-bezier(.2,.8,.3,1) both}
/* a real-but-tiny count must still paint. Floored here rather than in JS so the
   renderer can emit the honest percentage; the zero row is excluded explicitly. */
.hrv-bar-row:not(.is-zero) .hrv-bar > i{min-width:4px}
.hrv-bar-row.is-zero .hrv-bar > i{min-width:0}
@keyframes hrvBar{from{transform:scaleX(0)}to{transform:scaleX(1)}}
/* tabular-nums stops the count column jittering between 9 and 12 */
.hrv-bar-n{font-family:var(--ff-mono);font-size:12px;font-weight:700;color:var(--ink);text-align:right;font-variant-numeric:tabular-nums}
.hrv-bar-p{font-size:11.5px;color:var(--faint);text-align:right;font-variant-numeric:tabular-nums}
.hrv-bar-row[aria-pressed=true]{background:var(--hrv-cw);box-shadow:inset 0 0 0 1px var(--hrv-cb)}
.hrv-bar-row[aria-pressed=true] .hrv-bar-l{color:var(--ink)}
/* zero-count rows stay (the SHAPE of the distribution is information) but recede */
.hrv-bar-row.is-zero{opacity:.55;cursor:default;pointer-events:none}

/* ---- 2.3 filter / sort bar ---- */
/* Chips and bars are TWO affordances onto ONE filter state — clicking 5* in either
   must update both. The chip row is the touch path, the bars the scanning path. */
.hrv-tools{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:11px 16px;border-top:1px solid var(--border);background:var(--surface-mono)}
.hrv-chips{display:flex;gap:6px;min-width:0;overflow-x:auto;scrollbar-width:none;
  -webkit-overflow-scrolling:touch;padding:1px 0}
.hrv-chips:empty{display:none}
.hrv-chips::-webkit-scrollbar{display:none}
.hrv-chip{
  display:inline-flex;align-items:center;gap:5px;flex:none;white-space:nowrap;
  padding:6px 11px;border-radius:999px;border:1px solid var(--border);background:var(--card);
  font:inherit;font-size:12.5px;font-weight:600;color:var(--muted);cursor:pointer;
  transition:border-color .12s,color .12s,background .12s;
}
.hrv-chip svg{width:11px;height:11px;fill:currentColor;stroke:none;flex:none}
.hrv-chip .n{font-family:var(--ff-mono);font-size:11px;font-weight:700;color:var(--faint)}
.hrv-chip:hover{border-color:var(--border-strong);color:var(--ink)}
.hrv-chip.is-on{background:var(--hrv-cw);border-color:var(--hrv-cb);color:var(--hrv-cs)}
.hrv-chip.is-on .n{color:inherit;opacity:.75}
.hrv-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.hrv-tools-r{display:flex;align-items:center;gap:8px;margin-left:auto;flex:none}
.hrv-sort{display:inline-flex;align-items:center;gap:6px}
.hrv-sort-l{font-size:12px;color:var(--muted);font-weight:600}
/* the chevron is a data URI and therefore CANNOT inherit currentColor — it needs an
   explicit twin per theme or it is a black glyph on a dark control. */
.hrv-sel{height:32px;padding:0 26px 0 10px;border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--card) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23656D76' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E") no-repeat right 8px center/11px;
  font:inherit;font-size:12.5px;color:var(--ink);cursor:pointer;appearance:none;-webkit-appearance:none;outline:none}
:root[data-theme=dark] .hrv-sel{
  background:var(--card) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239DA7B3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E") no-repeat right 8px center/11px}
.hrv-sel:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-wash)}
.hrv-fnote{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12.5px;color:var(--muted);padding:2px 2px 0}
.hrv-fnote b{color:var(--ink);font-family:var(--ff-mono)}
.hrv-fclear{background:none;border:none;padding:0;font:inherit;font-size:12.5px;font-weight:700;color:var(--accent);cursor:pointer}
.hrv-fclear:hover{text-decoration:underline}

/* ---- 2.4 the review CARD ---- */
/* A card wears its own rating's hue in exactly two places: the star glyphs and a 3px
   left rule. The body stays --card. Ten mixed cards therefore read as ten small
   coloured star runs on a neutral field — a signal, not a rainbow.
   scroll-margin-top clears the 56px sticky topbar for #rv-<id> deep links. */
.hrv-card{padding:16px 18px;position:relative;border-left:3px solid var(--hrv-c);scroll-margin-top:78px}
.hrv-c-head{display:flex;align-items:flex-start;gap:11px}
.hrv-c-av{flex:none;text-decoration:none}
.hrv-c-who{flex:1;min-width:0}
.hrv-c-l1{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}
.hrv-c-name{font-size:14.5px;font-weight:700;color:var(--ink);text-decoration:none;letter-spacing:-.01em;overflow-wrap:anywhere}
.hrv-c-name:hover{color:var(--accent);text-decoration:underline}
.hrv-c-cty{display:inline-flex;align-items:center;gap:3px;flex:none;
  font-size:11px;font-weight:600;color:var(--muted);
  background:var(--surface-mono);border:1px solid var(--border);border-radius:999px;padding:1px 8px 1px 6px}
.hrv-c-pin{width:10px;height:10px;stroke:currentColor;fill:none;stroke-width:2;flex:none}
.hrv-c-l2{display:flex;align-items:center;gap:5px;flex-wrap:wrap;margin-top:2px;
  font-family:var(--ff-mono);font-size:11.5px;color:var(--muted)}
.hrv-c-handle{color:var(--accent);text-decoration:none}
.hrv-c-handle:hover{text-decoration:underline}
.hrv-c-sep{color:var(--faint)}
.hrv-c-time{white-space:nowrap}
.hrv-c-edited{color:var(--faint);white-space:nowrap}
.hrv-c-menu-wrap{position:relative;flex:none;margin-left:4px}
.hrv-c-menu{width:30px;height:30px;border:none;background:none;border-radius:50%;color:var(--muted);
  display:grid;place-items:center;cursor:pointer;font-size:17px;line-height:1;transition:background .12s,color .12s}
.hrv-c-menu:hover{background:var(--surface-mono);color:var(--ink)}
.hrv-c-rate{display:flex;align-items:center;gap:9px;margin-top:11px}
/* the verdict WORD is channel 3 of the colour-blind redundancy — never drop it.
   It uses --hrv-cs (deep ramp), not --hrv-c: tier 3 vivid is 3.4:1 on white and
   would fail AA as text. Rule: --hrv-c colours SHAPES, --hrv-cs colours TEXT. */
.hrv-c-verdict{font-size:12px;font-weight:700;color:var(--hrv-cs);letter-spacing:.01em}
.hrv-c-title{margin:9px 0 0;font-size:15px;font-weight:700;letter-spacing:-.01em;color:var(--ink);line-height:1.35;overflow-wrap:anywhere}
.hrv-c-body{margin-top:6px;font-size:14px;line-height:1.6;color:var(--ink);
  white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere}
.hrv-c-body.is-clamped{display:-webkit-box;-webkit-line-clamp:6;line-clamp:6;-webkit-box-orient:vertical;overflow:hidden}
.hrv-c-expand{margin-top:5px;background:none;border:none;padding:2px 0;font:inherit;
  font-size:12.5px;font-weight:700;color:var(--accent);cursor:pointer}
.hrv-c-img{display:block;margin-top:11px;padding:0;border:1px solid var(--border);border-radius:8px;
  overflow:hidden;background:var(--surface-mono);cursor:zoom-in;max-width:280px;width:100%;appearance:none}
.hrv-c-img img{display:block;width:100%;height:auto;max-height:220px;object-fit:cover}
.hrv-c-img:hover{border-color:var(--border-strong)}
.hrv-c-foot{display:flex;align-items:center;gap:6px;flex-wrap:wrap;row-gap:4px;
  margin-top:12px;padding-top:11px;border-top:1px solid var(--border)}
.hrv-c-like,.hrv-c-share,.hrv-c-report{
  display:inline-flex;align-items:center;gap:6px;
  padding:6px 10px;border-radius:var(--radius-sm);border:none;background:none;
  font:inherit;font-size:12.5px;font-weight:600;color:var(--muted);cursor:pointer;
  transition:background .12s,color .12s}
.hrv-c-like svg,.hrv-c-share svg,.hrv-c-report svg{width:15px;height:15px;stroke-width:2;flex:none}
.hrv-c-like:hover,.hrv-c-share:hover{background:var(--surface-mono);color:var(--accent)}
.hrv-c-report:hover{background:var(--surface-mono);color:var(--danger)}
.hrv-c-like[aria-pressed=true]{color:var(--accent)}
.hrv-c-like[aria-pressed=true] svg{fill:var(--accent-wash)}
.hrv-c-like .n{font-family:var(--ff-mono);font-size:11.5px;font-weight:700}
.hrv-c-views{display:inline-flex;align-items:center;gap:5px;margin-left:auto;flex:none;
  font-size:11.5px;color:var(--faint)}
.hrv-c-views b{font-family:var(--ff-mono);color:var(--muted)}
/* the reviewer's own card, so they can find it while scrolling */
.hrv-card.is-mine{box-shadow:inset 0 0 0 1px var(--hrv-cb),var(--shadow)}

/* Owner reply — deliberately echoes the .hcmt-replies left rule so it reads as the
   same "reply" grammar users already know from comments. */
.hrv-reply{margin:12px 0 0 4px;padding:10px 0 2px 13px;border-left:2px solid var(--hrv-cb)}
.hrv-reply-head{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:12px;color:var(--muted)}
.hrv-reply-head b{font-size:13px;color:var(--ink);font-weight:700}
.hrv-reply-head time{font-family:var(--ff-mono);font-size:11.5px}
.hrv-reply-tag{font-family:var(--ff-mono);font-size:10px;font-weight:700;letter-spacing:.03em;
  text-transform:uppercase;color:var(--accent);background:var(--accent-wash);
  border:1px solid rgba(9,105,218,.28);border-radius:999px;padding:1px 7px}
:root[data-theme=dark] .hrv-reply-tag{border-color:rgba(88,166,255,.30)}
.hrv-reply-body{margin-top:5px;font-size:13.5px;line-height:1.55;color:var(--ink);
  white-space:pre-wrap;word-break:break-word}

/* ---- 2.5 empty state + guest gate ---- */
.hrv-empty{text-align:center;padding:38px 22px;color:var(--muted)}
.hrv-empty-ic{width:52px;height:52px;margin:0 auto 13px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface-mono);border:1px solid var(--border);color:var(--faint)}
.hrv-empty h3{margin:0 0 6px;font-size:16px;font-weight:700;color:var(--ink);letter-spacing:-.01em}
.hrv-empty p{margin:0 auto 16px;font-size:13.5px;line-height:1.55;max-width:42ch}
.hrv-empty b{color:var(--ink);font-weight:600}
.hrv-gate{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:14px 16px}
.hrv-gate-t{flex:1;min-width:150px;font-size:13.5px;font-weight:600;color:var(--ink)}

/* ---- 2.6 the owner's strip on /@user/reviews ---- */
/* /@user/reviews is a PUBLIC page the owner also lands on, so the one thing they cannot do from it
   is the thing they came to do: reply, release a held review, read a report. Until now the only path
   was a 12.5px ghost "Manage" tucked in the sort row of .hrv-tools, beside a filter control — an
   owner action shelved with the reader's controls. This is the same link given its own strip at the
   top of the page, so it is the first thing the owner sees and the tools bar goes back to being one
   audience's row. Wear it as `class="card hrv-ownerbar"`, exactly like .hrv-gate.
   NO --hrv-c anywhere on it. Every other bordered block on this page carries a rating hue; this one
   is about the page, not about a score, and tinting it would put a sixth coloured shape in a system
   whose whole rule is one hue per surface. --accent-wash is the "this is yours / act on it" tone
   already used by .hrv-reply-tag and .rvm-stat.is-act.
   OWNER-ONLY IS THE RENDERER'S JOB — see the note on .hrv-tabhint. Do not gate this in CSS. */
.hrv-ownerbar{display:flex;align-items:center;gap:11px;flex-wrap:wrap;padding:12px 14px}
.hrv-ownerbar-ic{flex:none;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;
  background:var(--accent-wash);color:var(--accent)}
.hrv-ownerbar-ic svg{width:16px;height:16px;display:block;fill:none;stroke:currentColor;stroke-width:2}
/* 1 1 190px, not .hrv-gate-t's flex:1 — this block has two lines of copy, so it must claim a sane
   width before the button is allowed to sit beside it, and drop the button onto its own row below
   190px instead of shredding the sentence into four words per line. min-width:0 keeps it shrinkable
   once it is on that row: nothing here may widen the card. */
.hrv-ownerbar-t{flex:1 1 190px;min-width:0}
.hrv-ownerbar-t b{display:block;font-size:13.5px;font-weight:700;color:var(--ink);letter-spacing:-.01em}
.hrv-ownerbar-t span{display:block;margin-top:2px;font-size:12.5px;line-height:1.45;color:var(--muted);overflow-wrap:anywhere}
.hrv-ownerbar .btn{flex:none}

/* ---------- 3. WRITE / EDIT composer ---------- */
/* Inline card, NOT a modal: modals suppress reviews, an always-visible star row
   invites them. Collapses to a single row until focused. */
.hrv-compose{padding:0;overflow:hidden}
.hrv-open{display:flex;align-items:center;gap:11px;width:100%;padding:13px 16px;
  background:none;border:none;font:inherit;text-align:left;cursor:pointer;transition:background .12s}
.hrv-open:hover{background:var(--surface-mono)}
/* one-property tease: hovering the collapsed row lights the grey track light-green,
   which makes the affordance obvious with zero extra copy. */
.hrv-open:hover .hrv-stars-bg{color:var(--hrv-4)}
.hrv-open-t{flex:1;min-width:0;font-size:13.5px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hrv-form{padding:16px 18px 14px}
.hrv-f-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px}
.hrv-f-head h3{margin:0;font-size:15.5px;font-weight:700;letter-spacing:-.01em}
.hrv-f-rate{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}
/* ONE tab stop (role=radiogroup, tabindex=0). Hovering star n lights 1..n and sets
   --hrv-c on .hrv-pick to THAT tier's hue — the whole run is one colour, never a
   red-orange-amber-green gradient across the row. Biggest anti-traffic-light rule
   in the composer. The label beside it is the non-hue channel. */
.hrv-pick{display:inline-flex;gap:4px;border-radius:var(--radius-sm)}
.hrv-pick:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.hrv-pick-s{width:38px;height:38px;border:none;background:none;padding:0;cursor:pointer;
  display:grid;place-items:center;border-radius:8px;color:var(--hrv-track-0);
  transition:color .1s,transform .1s,background .12s}
.hrv-pick-s svg{width:28px;height:28px;fill:currentColor;stroke:none;pointer-events:none}
.hrv-pick-s:hover{background:var(--surface-mono)}
.hrv-pick-s:active{transform:scale(.9)}
.hrv-pick-s.is-lit{color:var(--hrv-c,var(--hrv-3))}
.hrv-pick-lbl{font-size:13px;font-weight:700;color:var(--hrv-cs,var(--muted));min-width:9ch}
.hrv-f-title,.hrv-f-body{width:100%;border:1px solid var(--border);border-radius:8px;
  background:var(--surface-mono);color:var(--ink);font:inherit;outline:none;
  transition:border-color .12s,background .12s,box-shadow .12s}
.hrv-f-title{height:40px;padding:0 12px;font-size:14px;font-weight:600;margin-bottom:9px}
.hrv-f-body{padding:11px 12px;font-size:14px;line-height:1.55;resize:vertical;min-height:104px}
.hrv-f-title:focus,.hrv-f-body:focus{border-color:var(--accent);background:var(--card);box-shadow:0 0 0 3px var(--accent-wash)}
.hrv-f-thumbs{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.hrv-f-thumbs:empty{display:none}
.hrv-thumb{position:relative;width:84px;height:84px;border-radius:8px;overflow:hidden;
  border:1px solid var(--border);background:var(--surface-mono);flex:none}
.hrv-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.hrv-thumb .x{position:absolute;top:4px;right:4px;width:22px;height:22px;border:none;border-radius:50%;
  background:rgba(13,17,23,.72);color:#fff;display:grid;place-items:center;cursor:pointer;z-index:1}
/* dark scrim while the image is still uploading; the submit button is disabled in
   the same state, so a review can never post with a half-uploaded photo. */
.hrv-thumb.is-up::after{content:"";position:absolute;inset:0;background:rgba(13,17,23,.45)}
.hrv-f-foot{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:12px}
.hrv-f-photo{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:var(--radius-sm);
  border:1px dashed var(--border-strong);background:none;font:inherit;font-size:12.5px;font-weight:600;
  color:var(--muted);cursor:pointer;transition:border-color .12s,color .12s,border-style .12s}
.hrv-f-photo:hover{border-color:var(--accent);color:var(--accent);border-style:solid}
.hrv-f-photo svg{width:15px;height:15px;stroke-width:2;flex:none}
.hrv-f-count{font-family:var(--ff-mono);font-size:11.5px;color:var(--faint);margin-left:auto}
.hrv-f-count.is-near{color:var(--amber)}
.hrv-f-count.is-over{color:var(--danger);font-weight:700}
.hrv-f-note{margin:11px 0 0;font-size:11.5px;line-height:1.5;color:var(--faint)}
.hrv-f-hint{margin:8px 0 0;font-size:12px;line-height:1.45;color:var(--muted)}

/* "You already reviewed this profile" — a receipt with a live preview, not a
   disabled form and not a toast. */
.hrv-mine{padding:13px 16px;border-left:3px solid var(--hrv-c)}
.hrv-mine-l{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.hrv-mine-tag{font-family:var(--ff-mono);font-size:10.5px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;color:var(--hrv-cs);background:var(--hrv-cw);
  border:1px solid var(--hrv-cb);border-radius:999px;padding:2px 9px}
.hrv-mine-x{color:var(--faint)}
.hrv-mine-t{font-family:var(--ff-mono);font-size:11.5px;color:var(--muted)}
.hrv-mine-b{margin:8px 0 0;font-size:13.5px;line-height:1.5;color:var(--muted);
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.hrv-mine-a{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:11px}
.hrv-mine-del{background:none;border:none;padding:6px 2px;font:inherit;font-size:12.5px;
  font-weight:600;color:var(--muted);cursor:pointer}
.hrv-mine-del:hover{color:var(--danger)}
.hrv-mine-go{margin-left:auto;font-size:12.5px;font-weight:600;color:var(--accent);text-decoration:none}
.hrv-mine-go:hover{text-decoration:underline}

/* ---------- 5. the HELD state (owner / reviewer only — never public) ---------- */
/* The negative margins pull the banner flush to the card edges so it reads as a
   status ribbon ON the card, not a box inside it. Keep them in sync with
   .hrv-card's padding (16px 18px) and its 680px override (14px). */
.hrv-card.is-held{border-left-color:var(--amber)}
.hrv-hold{display:flex;gap:10px;align-items:flex-start;
  margin:-16px -18px 14px;padding:11px 18px;
  background:var(--amber-bg);border-bottom:1px solid #E4C77B}
:root[data-theme=dark] .hrv-hold{background:rgba(210,153,34,.10);border-bottom-color:rgba(210,153,34,.34)}
.hrv-hold-ic{width:17px;height:17px;flex:none;margin-top:1px;color:var(--amber);stroke-width:2;fill:none}
.hrv-hold-b{min-width:0}
.hrv-hold-b b{display:block;font-size:12.5px;font-weight:700;color:var(--amber);letter-spacing:-.01em}
:root[data-theme=dark] .hrv-hold-b b{color:#E0AE3C}
.hrv-hold-b p{margin:3px 0 0;font-size:12.5px;line-height:1.5;color:var(--muted)}
.hrv-hold-b p b{display:inline;color:var(--ink);font-weight:600}
.hrv-hold-a{display:inline-block;margin-top:6px;font-size:12.5px;font-weight:700;color:var(--accent);text-decoration:none}
.hrv-hold-a:hover{text-decoration:underline}

/* ===================== 4. REVIEW MANAGER (/reviews, .rvm-*) =====================
   A WORK QUEUE, not a dashboard: dense rows, one reply field per row, everything
   actionable without leaving the page. The stat strip is numbers on one line.
   This page is a different blade — it inherits the dark .btn-ghost fix already in
   this sheet (see the :root[data-theme=dark] .btn-ghost rule above), so it must
   NOT ship its own copy. */
.rvm-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.rvm-head-l{flex:1;min-width:0}
.rvm-head h1{margin:0;font-size:21px;font-weight:800;letter-spacing:-.02em}
.rvm-head .sub{margin:2px 0 0;font-size:12.5px;color:var(--muted)}
/* 1px gap over a --border background = hairline dividers with no per-cell borders
   to keep in sync. "On hold" and "Awaiting reply" are BUTTONS — the two numbers an
   owner actually acts on. */
.rvm-stats{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:1px;
  background:var(--border);border:1px solid var(--border);overflow:hidden;margin-bottom:14px}
.rvm-stat{background:var(--card);padding:13px 14px;display:flex;flex-direction:column;gap:2px;min-width:0;
  border:none;font:inherit;text-align:left;align-items:flex-start}
.rvm-stat .v{font-family:var(--ff-mono);font-size:19px;font-weight:700;color:var(--ink);letter-spacing:-.01em;line-height:1.1}
.rvm-stat .l{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.rvm-stat .hrv-stars{margin-top:3px}
.rvm-stat.is-warn .v{color:var(--amber)}
.rvm-stat.is-act .v{color:var(--accent)}
.rvm-stat.is-warn,.rvm-stat.is-act{cursor:pointer}
.rvm-stat.is-warn:hover,.rvm-stat.is-act:hover{background:var(--surface-mono)}
.rvm-stat:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* sticky toolbar. top:64px = the 56px sticky .topbar + an 8px gap; z-index 30 keeps
   it UNDER the topbar (60) so it slides beneath rather than over. */
.rvm-bar{position:sticky;top:64px;z-index:30;display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:9px 12px;margin-bottom:12px;background:var(--card);
  border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}
.rvm-tabs{display:flex;gap:3px;min-width:0;overflow-x:auto;scrollbar-width:none}
.rvm-tabs::-webkit-scrollbar{display:none}
/* .is-on reuses the profile tab's inverted-ink treatment so the two tab systems
   feel like one product. --ink/--card swap between themes, so it works in both. */
.rvm-tab{display:inline-flex;align-items:center;gap:6px;flex:none;white-space:nowrap;
  padding:7px 11px;border:none;background:none;border-radius:var(--radius-sm);
  font-family:var(--ff-mono);font-size:12.5px;font-weight:500;color:var(--muted);cursor:pointer;
  transition:background .12s,color .12s}
.rvm-tab:hover{background:var(--surface-mono);color:var(--ink)}
.rvm-tab.is-on{background:var(--ink);color:var(--card)}
.rvm-tab .n{font-size:11px;font-weight:700;opacity:.72}
.rvm-tab.is-warn .n{color:var(--amber);opacity:1}
.rvm-tab.is-on.is-warn .n{color:var(--amber-bg)}
.rvm-bar-r{display:flex;align-items:center;gap:8px;margin-left:auto;flex-wrap:wrap}
.rvm-find{display:inline-flex;align-items:center;gap:7px;height:32px;padding:0 11px;
  border:1px solid var(--border);border-radius:999px;background:var(--surface-mono);min-width:0}
.rvm-find svg{width:14px;height:14px;color:var(--faint);flex:none}
.rvm-find input{border:none;background:none;outline:none;font:inherit;font-size:12.5px;color:var(--ink);width:170px;min-width:0}
.rvm-find:focus-within{border-color:var(--accent);background:var(--card)}

/* ---- the row ---- */
.rvm-row{padding:0;margin-bottom:10px;border-left:3px solid var(--hrv-c);overflow:hidden}
.rvm-row.is-held{border-left-color:var(--amber);background:var(--amber-bg)}
:root[data-theme=dark] .rvm-row.is-held{background:rgba(210,153,34,.07)}
/* j/k keyboard cursor — this is what makes the page a tool rather than a dashboard */
.rvm-row.is-sel{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-wash)}
.rvm-r-main{display:flex;gap:11px;padding:13px 15px}
.rvm-r-av{flex:none}
.rvm-r-b{flex:1;min-width:0}
.rvm-r-top{display:flex;align-items:center;gap:7px;flex-wrap:wrap;row-gap:5px}
.rvm-r-name{font-size:13.5px;font-weight:700;color:var(--ink);text-decoration:none}
.rvm-r-name:hover{color:var(--accent);text-decoration:underline}
.rvm-r-handle{font-family:var(--ff-mono);font-size:11.5px;color:var(--accent)}
.rvm-r-time{font-family:var(--ff-mono);font-size:11.5px;color:var(--muted);white-space:nowrap}
.rvm-r-flags{display:inline-flex;gap:5px;margin-left:auto;flex:none}
.rvm-flag{font-family:var(--ff-mono);font-size:10px;font-weight:700;letter-spacing:.03em;
  text-transform:uppercase;border-radius:999px;padding:2px 8px;border:1px solid transparent}
/* the three literal hexes here are already in the palette (.pf-badge c-amber/c-red/
   c-blue); dark needs softer edges or they read as glowing outlines. */
.rvm-flag.is-held{color:var(--amber);background:var(--amber-bg);border-color:#E4C77B}
.rvm-flag.is-reported{color:var(--danger);background:rgba(207,34,46,.08);border-color:#F0B4B4}
.rvm-flag.is-new{color:var(--accent);background:var(--accent-wash);border-color:#9DC3F0}
:root[data-theme=dark] .rvm-flag.is-held{border-color:rgba(210,153,34,.38)}
:root[data-theme=dark] .rvm-flag.is-reported{background:rgba(248,81,73,.14);border-color:rgba(248,81,73,.34)}
:root[data-theme=dark] .rvm-flag.is-new{border-color:rgba(88,166,255,.32)}
.rvm-r-title{margin-top:5px;font-size:14px;font-weight:700;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rvm-r-txt{margin-top:3px;font-size:13px;line-height:1.5;color:var(--muted);
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;cursor:pointer}
.rvm-row.is-open .rvm-r-txt{-webkit-line-clamp:unset;line-clamp:unset;display:block;white-space:pre-wrap;color:var(--ink)}
.rvm-r-metrics{display:flex;gap:14px;flex-wrap:wrap;margin-top:9px}
.rvm-r-metrics span{display:inline-flex;align-items:center;gap:5px;
  font-family:var(--ff-mono);font-size:11.5px;color:var(--muted)}
.rvm-r-metrics svg{width:13px;height:13px;stroke-width:2;color:var(--faint)}
.rvm-r-metrics .is-warn,.rvm-r-metrics .is-warn svg{color:var(--danger)}
.rvm-r-side{display:flex;align-items:flex-start;gap:6px;flex:none}
.rvm-r-menu{width:30px;height:30px;border:none;background:none;border-radius:var(--radius-sm);
  color:var(--muted);display:grid;place-items:center;cursor:pointer;font-size:17px;line-height:1}
.rvm-r-menu:hover{background:var(--surface-mono);color:var(--ink)}
/* the manager's own hold strip: same language as .hrv-hold but full-bleed inside a
   padding:0 row, so no negative margins. */
.rvm-hold{display:flex;gap:10px;align-items:flex-start;padding:11px 15px;
  border-top:1px solid #E4C77B;background:rgba(154,103,0,.06)}
:root[data-theme=dark] .rvm-hold{border-top-color:rgba(210,153,34,.34);background:rgba(210,153,34,.08)}
.rvm-hold svg{width:17px;height:17px;flex:none;margin-top:1px;color:var(--amber);stroke-width:2;fill:none}
.rvm-hold b{display:block;font-size:12.5px;font-weight:700;color:var(--amber)}
:root[data-theme=dark] .rvm-hold b{color:#E0AE3C}
.rvm-hold p{margin:3px 0 0;font-size:12.5px;line-height:1.5;color:var(--muted)}
.rvm-hold p b{display:inline;color:var(--ink);font-weight:600}
.rvm-reply{padding:11px 15px 13px;border-top:1px solid var(--border)}
.rvm-reply-in{width:100%;padding:9px 11px;border:1px solid var(--border);border-radius:8px;
  background:var(--surface-mono);font:inherit;font-size:13.5px;line-height:1.5;color:var(--ink);
  resize:vertical;outline:none;min-height:62px}
.rvm-reply-in:focus{border-color:var(--accent);background:var(--card);box-shadow:0 0 0 3px var(--accent-wash)}
.rvm-reply-f{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:8px}
.rvm-reply-n{font-size:11.5px;color:var(--faint);flex:1;min-width:140px}
.rvm-replied{display:flex;align-items:center;gap:8px;padding:10px 15px;
  border-top:1px solid var(--border);background:var(--surface-mono)}
.rvm-replied-t{flex:1;min-width:0;font-size:12.5px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rvm-replied-e{background:none;border:none;padding:2px 0;font:inherit;font-size:12.5px;
  font-weight:700;color:var(--accent);cursor:pointer;flex:none}
.rvm-replied-e:hover{text-decoration:underline}

/* ---------- 6. responsive, 360 -> 1280 ----------
   Breakpoints match the ones already in this file (820 drawer / 680 / 560 / 430) so
   nothing lands between two systems.
   TWO OVERFLOW GUARANTEES, for QA:
   1. .hrv-sum is flex-wrap + max-width:100%; its only flex:none child is the 81px
      star run; the text span is min-width:0 + overflow-wrap:anywhere. It can wrap to
      two lines INSIDE its pill; it can never widen .profile-id (flex:1 1 260px).
   2. .hrv-bar track is minmax(0,1fr) between three fixed px columns. At 360px:
      26+8+track+8+36 = 332 -> track 254px. It shrinks to zero before it pushes.
      The 36px count column is the same floor as the desktop grid and for the same
      reason: five digits of 12px tabular mono measure ~36px, and a grid item that
      does not fit its column overflows the column rather than widening it.
   Also: .hrv-chips and .rvm-tabs scroll INSIDE themselves (overflow-x:auto, hidden
   scrollbar), the same pattern .feed-tabs uses. The body never scrolls sideways. */
@media(max-width:820px){
  .rvm-stats{grid-template-columns:repeat(3,minmax(0,1fr))}
  .rvm-bar{top:56px}
}
@media(max-width:680px){
  /* 680, not 560 with the chips: this is where .tab already goes to padding:9px 12px, taking every
     tab from 31px to 35px and .feed-tabs from 43px to 47px. The hint follows its neighbours or it
     sits 5px short in a bar that grew around it. Measured after: hint 34px, tabs 35px, bar 47px —
     i.e. the bar is exactly the height it is today with no hint in it. */
  .hrv-tabhint{padding:8px 12px}
  .hrv-hero{grid-template-columns:minmax(0,1fr);gap:16px;padding:16px}
  /* the score block goes horizontal — vertical wastes a third of a phone screen
     before a single review is visible. flex-wrap makes it work with or without the
     optional .hrv-score-meta wrapper. */
  .hrv-score{flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:6px 14px;padding:13px 16px}
  .hrv-score-v{font-size:34px;flex:none}
  .hrv-score-of{display:none}
  .hrv-score-meta{align-items:flex-start;gap:4px}
  .hrv-tools{padding:10px 12px}
  .hrv-card{padding:14px}
  .hrv-hold{margin:-14px -14px 12px;padding:10px 14px}
  .hrv-c-title{font-size:14.5px}
  .hrv-c-img{max-width:100%}
  .hrv-form{padding:14px}
  .rvm-r-main{padding:12px}
  .rvm-hold,.rvm-reply,.rvm-replied{padding-left:12px;padding-right:12px}
}
@media(max-width:560px){
  .hrv-bars{gap:6px}
  /* 26/1fr/36 — the percent column is the first thing to go; the count column keeps
     its 36px floor, because a 5-figure count is 5 figures at 360px too */
  .hrv-bar-row{grid-template-columns:26px minmax(0,1fr) 36px;gap:8px;padding:6px 4px}
  .hrv-bar-p{display:none}
  .hrv-bar{height:8px}
  /* WCAG 2.5.8 target size — these are 25-30px otherwise, on the one breakpoint
     guaranteed to be touch. */
  .hrv-chip{padding:8px 12px}
  /* the strip's copy has already wrapped the button onto its own row by here (flex-basis 190px), and
     a hug-width button alone on a line is the smallest target on the page — take the full row. */
  .hrv-ownerbar .btn{width:100%}
  .hrv-c-like,.hrv-c-share,.hrv-c-report{padding:9px 10px}
  .hrv-c-menu,.rvm-r-menu{width:34px;height:34px}
  .hrv-pick-s{width:44px;height:44px}
  .hrv-pick-s svg{width:32px;height:32px}
  /* 16px or iOS Safari force-zooms the page on focus and never zooms back out —
     the same fix already applied to the comment and collab fields above. Every
     control here carries an explicit height, so nothing reflows. */
  .hrv-f-title,.hrv-f-body,.rvm-reply-in,.rvm-find input{font-size:16px}
  .rvm-stats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .rvm-bar-r{width:100%;margin-left:0}
  .rvm-find{flex:1}
  .rvm-find input{width:100%}
  .hrv-tools-r{width:100%;margin-left:0}
  .hrv-sort{flex:1}
  .hrv-sort .hrv-sel{flex:1;min-width:0}
  /* Only the padding narrows here now. "out of 5" and the long copy are dropped at EVERY
     width by the base rule, so restating them at a breakpoint would just be a second place
     to keep in step. */
  .hrv-sum{padding:4px 10px 4px 8px;gap:0 6px}
}
@media(max-width:430px){
  /* icons only; the aria-label still carries the name */
  .hrv-c-share .lbl,.hrv-c-report .lbl{display:none}
  .hrv-c-foot{gap:2px}
  .hrv-c-cty{max-width:11ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .hrv-score-v{font-size:30px}
  .rvm-r-title{white-space:normal}
  .rvm-r-flags{margin-left:0;flex-basis:100%}
  /* The pill stays hug-width even at 360px — it is now ~150px, so stretching it edge-to-edge
     would make a small fact look like a call to action. The long/short swap and "out of 5"
     are already handled by the base rule; only the separator goes, because at this width
     "3.8  5 reviews" is still unambiguous and the middot is the least useful pixel left. */
  .hrv-sum-sep{display:none}
  .hrv-sum{justify-content:flex-start}
}

/* ============ THERE IS DELIBERATELY NO .avail RULE IN THIS SHEET ============
   Absence by decision, not by oversight — and this comment exists so that a grep
   for ".avail" in devsocial.css returns a reason instead of returning nothing.

   The profile "Open to work" badge is defined exactly ONCE, in the inline <style>
   of resources/views/ux/profile.blade.php, under a comment marked "THIS IS THE ONLY
   DEFINITION OF .avail". profile.blade.php links /ux/devsocial.css and only then
   opens that <style>, so the blade rules are parsed after everything here and win
   every equal-specificity tie by source order. Checked in the browser rather than
   assumed: on /@priya_sharma every .avail rule the page loads — .avail, .avail svg,
   .avail .avail-t, the dark override, the 430px override — reports this sheet's
   inline sibling as its origin, and this sheet contributes none of them.

   Its markup is span.avail containing an inline briefcase <svg> and span.avail-t,
   both emitted by openToWorkHtml(). THERE IS NO DOT, and the absence is the design,
   not an omission: a small filled circle is this product's vocabulary for the
   decorative free-badge catalog — .pf-badge .dot, which is styled in that same
   blade <style> a few lines above .avail and NOT here, an earlier draft of this
   paragraph said "styled in THIS file" and it never was — whose catalog entry in
   config/badges.php is also labelled "Open to work" and can sit on the very same
   header. This sheet contributes exactly one rule to that component and it is not a
   badge rule: `.pf-badge > svg`, the glyph SIZE and paint, in the block headed
   "profile badge row: the catalog glyph" above. Box in the blade, glyph here; that
   block says what breaks if the two ever both define one. Give the real badge a dot and the hiring signal becomes
   indistinguishable from the sticker next to it. An earlier revision of this
   comment described the badge as wrapping a "span.d" dot, which it does not and did
   not; anyone who trusted that line and restored one would have rebuilt precisely
   the collision the blade block was rewritten to end.

   What used to sit at the foot of this file: a second, ANIMATED .avail pill, its
   selector doubled to .avail.avail for no reason other than to out-specify the
   blade block. Effect on the product: the calm outlined badge the owner signed off
   on was unreachable, the pulsing lozenge rendered on every profile instead, and
   editing the rule in the file where it is obviously written did nothing. It also
   ran an infinite box-shadow animation, which cannot take the compositor fast path
   and therefore repainted the profile header for as long as the tab stayed open.
   That block is gone; profile.blade.php now owns the badge outright, and computed
   animation-name on the rendered badge is "none" in both themes.

   So: do not add a badge rule below this line. Only two outcomes are available.
   Written normally it is dead CSS, and the next person burns an afternoon on an
   edit that has no effect. Written with a specificity hack it works, and you have
   rebuilt the exact split-brain described above — two definitions, one reachable,
   neither safe to touch. Edit profile.blade.php instead.

   THEMING STILL DEPENDS ON THIS FILE AND MUST KEEP DOING SO. Three tokens declared
   at the top are load-bearing for a surface defined in another file, so changing any
   of them moves the badge silently, with no diff anywhere near it:

     --green (:root #1A7F37, :root[data-theme=dark] #3FB950) tints the briefcase
       glyph and NOTHING else. The label is --ink — measured #1F2328 light and
       #E6EDF3 dark — because green text is already spoken for by .biz-badge
       "Verified company", which sits two lines above it on the same card, and
       because #3FB950 label text out-glows the display name on #161B22. Re-point or
       drop --green and the glyph goes muddy on #0D1117; never reach for it to "fix"
       the label, which does not consume it.
     --radius (6px) is the badge's border-radius, deliberately the radius of the
       page's real surfaces rather than the 999px every ornament in the identity
       block uses. Round this token up and the badge quietly turns back into the
       capsule the redesign threw out, matching .pf-badge and .skill-bubble again.
     --ff-ui is named explicitly on the badge because its second instance renders
       inside the About section header, which is --ff-mono. Break --ff-ui and one
       flag comes out in two typefaces on one page.

   Only the wash and the border need explicit dark values, and those sit beside the
   rule in the blade as alpha over --green: 0.09 fill / 0.28 line light, 0.12 / 0.30
   dark. Measured at 360px and 1280px in both themes, the badge is 117x24 and 122x27
   in a 300px / 518px row — it never overflows and never adds a line to the name.
   ========================================================================== */

/* ==========================================================================
   Sponsored units — .hudr-ad (rendered by /ux/ads.js on home / explore /
   communities; the reels interstitial has its own full-screen styling in
   reels.blade.php because it lives on a dark video stage, not on a card).

   Design contract: one anatomy everywhere so a member learns "this shape =
   an ad" ONCE. Head row first (megaphone glyph + destination domain +
   SPONSORED chip) — the disclosure comes before the creative, always, and it
   is a bordered chip rather than loose microtype so it survives a busy
   creative sitting directly beneath it.

   Card chrome is drawn entirely from the theme tokens, so both themes work
   without a single hardcoded colour. The ONE exception is the control layer
   that sits ON a video/animated creative (play, sound, progress, badge):
   that layer is white-on-translucent-black in both themes on purpose,
   because its background is the advertiser's own footage, not our surface —
   the same reasoning the reels overlay already uses.

   The media box reserves 8:5 via aspect-ratio (config ads.ad.image_width/
   height is 800×500, and uploads are resized to it) — the unit's height is
   final before the creative's bytes arrive, so a slow creative can never
   shift the rail or the feed under the reader. ALL THREE creative kinds
   (image, video, animated) share that one reserved box; a video that turns
   out to be portrait is letterboxed inside it over a blurred copy of its own
   poster (.is-tall) rather than being allowed to resize the card or be
   cropped to a vertical slice.

   Motion: the CTA icon nudges on hover and the still creative scales a
   hair — both are switched off wholesale by the reduced-motion block at the
   end. Video autoplay is refused in JS under the same preference (see
   ads.js wireVideo), because CSS cannot stop a <video> from playing.

   These rules sit at the END of the sheet on purpose: .hudr-ad ties with
   .card/.rr-card at (0,1,0) and must win the padding fight by order.
   ========================================================================== */
/* flex:none is load-bearing: .rightrail is an overflowing flex COLUMN (sticky, max-height,
   overflow-y:auto), and a flex item with overflow:hidden gets min-height:0 — so without it the
   whole unit silently shrank to its 2px of borders whenever the rail was taller than the
   viewport, exactly the bug #houseAd's own flex-shrink:0 already guards against. */
.hudr-ad{padding:0;overflow:hidden;flex:none}
/* The head row is METADATA now, not a banner: one line of small muted text, no icon tile, no pill.
   "Sponsored" keeps its own class so it stays greppable and stays styled as a label, but it sits in
   the normal type ramp instead of mono-uppercase-in-a-chip. margin-left:auto is gone with the pill —
   brand and label now read as one sentence, which is what stops the row looking like ad furniture. */
.hudr-ad-head{display:flex;align-items:center;gap:5px;padding:10px 14px 8px;font-size:12px;line-height:1.3}
.hudr-ad-brand{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;color:var(--muted)}
.hudr-ad-dot{flex:none;color:var(--faint)}
.hudr-ad-flag{flex:none;font-weight:500;color:var(--muted)}

/* ---- the creative box: one silhouette for image / video / animated ---- */
.hudr-ad-media{position:relative;display:block;aspect-ratio:8/5;overflow:hidden;background:var(--surface-mono)}
.hudr-ad-media img,.hudr-ad-img{display:block;width:100%;height:100%;object-fit:cover}
/* The still creative used to scale on hover. Removed with the rest of the loud chrome — the feed's
   own cards do not animate under the cursor, and an ad that does is the one thing on screen behaving
   differently from everything around it. (.hudr-ad-img keeps its sizing from the shared
   `.hudr-ad-media img,.hudr-ad-img` rule above — only the transition and the hover rule went.) */
/* The click-through is a stretched transparent anchor rather than the box itself: video and
   animated creatives carry real <button> controls on top of the creative, and a button inside an
   anchor is not valid HTML. z-index 2 keeps it above the creative and below those controls. */
.hudr-ad-hit{position:absolute;inset:0;z-index:2}
.hudr-ad-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;background:#0D1117;z-index:1}
/* blurred self-backdrop, revealed only for a creative more upright than the 8:5 box */
.hudr-ad-vbg{position:absolute;inset:-8%;z-index:0;background-size:cover;background-position:center;filter:blur(20px) saturate(1.15) brightness(.7);opacity:0;transition:opacity .25s ease}
.hudr-ad-media.is-tall .hudr-ad-vbg{opacity:1}
.hudr-ad-media.is-tall .hudr-ad-vid{object-fit:contain;background:transparent}

/* ---- controls that sit ON the creative (white-on-black in BOTH themes, see the header) ---- */
.hudr-ad-ctl{position:absolute;z-index:3;display:grid;place-items:center;width:30px;height:30px;padding:0;border-radius:999px;border:1px solid rgba(255,255,255,.28);background:rgba(0,0,0,.52);color:#fff;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);transition:background .15s ease,border-color .15s ease}
.hudr-ad-ctl:hover{background:rgba(0,0,0,.72);border-color:rgba(255,255,255,.45)}
.hudr-ad-sound{right:9px;bottom:11px}
.hudr-ad-media.is-loud .hudr-ad-sound{background:var(--accent);border-color:var(--accent)}
.hudr-ad-big{position:absolute;z-index:3;left:50%;top:50%;transform:translate(-50%,-50%);display:grid;place-items:center;width:54px;height:54px;padding:0;border-radius:999px;border:1px solid rgba(255,255,255,.32);background:rgba(0,0,0,.55);color:#fff;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);transition:transform .18s ease,background .15s ease}
.hudr-ad-big:hover{background:rgba(0,0,0,.72);transform:translate(-50%,-50%) scale(1.06)}
.hudr-ad-big svg{margin-left:2px}
/* the progress rail doubles as the "this is a video" tell before the first frame paints */
.hudr-ad-prog{position:absolute;z-index:3;left:0;right:0;bottom:0;height:3px;background:rgba(255,255,255,.22)}
.hudr-ad-prog i{display:block;height:100%;width:0;background:var(--accent);border-radius:0 2px 2px 0;transition:width .15s linear}
.hudr-ad-time{position:absolute;z-index:3;left:9px;bottom:11px;font-family:var(--ff-mono);font-size:10.5px;font-weight:600;color:#fff;background:rgba(0,0,0,.52);border-radius:4px;padding:1px 6px;pointer-events:none}
.hudr-ad-time:empty{display:none}
.hudr-ad-badge{position:absolute;z-index:3;left:9px;top:9px;font-family:var(--ff-mono);font-size:9.5px;font-weight:700;letter-spacing:.06em;color:#fff;background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.22);border-radius:4px;padding:2px 6px;pointer-events:none}

.hudr-ad-body{padding:12px 15px 15px}
.hudr-ad-title{display:block;font-size:14px;font-weight:600;color:var(--ink);line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.hudr-ad-title:hover{color:var(--accent)}
/* body copy clamps at 3 lines — advertiser text is capped at 220 chars server-side, but a rail
   card must stay a card, not a column of prose */
.hudr-ad-text{margin:4px 0 12px;font-size:12.5px;color:var(--muted);line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
/* CTA — the advertiser's own label, so it must be the loudest thing under the creative. It stays
   a .btn (same radius, same accent, same focus ring as every other button on the site): only the
   weight and the padding are raised over .btn-sm, and an icon derived from the label trails it. */
.hudr-ad-cta{padding:8px 14px;font-size:13px;font-weight:600}
.hudr-ad-cta-i{display:inline-flex;align-items:center;transition:transform .18s ease}
.hudr-ad-cta-i svg{display:block}
.hudr-ad-cta:hover .hudr-ad-cta-i{transform:translateX(3px)}
/* feed variant only scales type/spacing — same anatomy, same label, same order */
.hudr-ad--feed .hudr-ad-head{padding:11px 16px 8px;font-size:12.5px}
.hudr-ad--feed .hudr-ad-body{padding:12px 16px 15px}
.hudr-ad--feed .hudr-ad-title{font-size:15px}
.hudr-ad--feed .hudr-ad-text{font-size:13px;margin:5px 0 12px}
.hudr-ad--feed .hudr-ad-cta{padding:9px 16px;font-size:13.5px}
.hudr-ad--feed .hudr-ad-big{width:60px;height:60px}
/* Phones get the feed unit only (the rail is display:none below 1024px), and there the creative is
   edge-to-edge, so the controls move in off the rounded corners. */
@media(max-width:560px){
  .hudr-ad--feed .hudr-ad-head{padding:10px 13px 8px}
  .hudr-ad--feed .hudr-ad-body{padding:11px 13px 13px}
  .hudr-ad--feed .hudr-ad-title{font-size:14.5px}
  .hudr-ad--feed .hudr-ad-text{font-size:12.5px}
  .hudr-ad-sound,.hudr-ad-time{bottom:12px}
  .hudr-ad--feed .hudr-ad-big{width:52px;height:52px}
}
/* A member who has asked for less motion gets a still card: no icon nudge, no creative scale, no
   transitions. The <video> is held on its poster by ads.js, which is the only place that CAN hold
   it — CSS has no way to refuse playback. */
@media(prefers-reduced-motion:reduce){
  .hudr-ad-img,.hudr-ad-cta-i,.hudr-ad-vbg,.hudr-ad-ctl,.hudr-ad-big,.hudr-ad-prog i{transition:none}
  .hudr-ad:hover .hudr-ad-img{transform:none}
  .hudr-ad-cta:hover .hudr-ad-cta-i{transform:none}
  .hudr-ad-big:hover{transform:translate(-50%,-50%)}
}

/* ---- @hudr's avatar mark ----------------------------------------------------------------
   public/ux/brand/hudr-mark.svg is drawn in ONE colour (black) and the PAGE flips it, not the
   SVG. Do not move this into the file — it cannot work there. An <img>-loaded SVG is a separate
   document with no access to the host page: its own @media (prefers-color-scheme) reads the
   SYSTEM setting, but this site's theme is a manual toggle that stamps data-theme on <html>
   (shell.js applyTheme()), so the SVG would be wrong for every member who has chosen against
   their OS. (prefers-reduced-motion is the opposite case and IS handled inside the file — that
   one really is an OS setting, so it reaches the image document.)

   The hook is the FILENAME, not a class. There is no shared avatar partial in this codebase —
   avatars are written inline as <img src="{{ avatar_url }}"> in a dozen blades and in shell.js /
   comments.js — so one attribute selector covers the profile header, feed, comments, chat and
   search with no per-surface change. It cannot collide: every uploaded file is stored under a v4
   UUID (AbstractUploadService::determineStoragePath) and a UUID is hex, so it can never contain
   h/u/r/m/k — no member can name their way into this rule. Deliberately not anchored to ".svg"
   or to a leading slash, so the match survives whatever the storage disk prefixes (/storage/…,
   the S3 host) and any ?v= cache-buster appended after it.

   ONE declaration, no theme test of its own: --mark-flip is a token declared beside --canvas and
   --ink in the :root[data-theme=dark] block, and is `none` everywhere else because an undefined
   custom property falls back. That is on purpose. The obvious version of this rule — repeating
   `:root[data-theme=dark]` here, plus an @media (prefers-color-scheme:dark) arm for the window
   before shell.js stamps the attribute — is WRONG, and measurably so: this sheet has no
   prefers-color-scheme token block at all, so the system preference alone never darkens the
   page. With data-theme absent and the OS dark, the page paints its LIGHT tokens
   (--canvas:#E1E5EB, measured) while that arm would invert the mark to white — an invisible
   white glyph on a white card, for the whole pre-boot window on every page, and permanently on
   any page that loads this sheet without shell.js. Sourcing the flip from the same token block
   the colours come from makes that class of drift impossible: however the page decides it is
   dark, the mark decides the same way at the same moment. */
img[src*="hudr-mark"]{filter:var(--mark-flip,none)}
/* ---- @hudr's OWL avatar ------------------------------------------------------------------
   The owl replaces the single-colour "hu" glyph above, and it needs the opposite technique. It is
   two-tone (--ink outline + --paper fill), so filter:invert() is not an option: it would turn the
   eye-whites black and the pupils white. The brand ships a file per theme, so the dark theme
   swaps the FILE via content:url(), sourced from --owl-src in the same token block as the colours.

   WHY NOT @media (prefers-color-scheme) INSIDE THE SVG — I tried it first and it is actively
   wrong here. This sheet has no system-preference token block, so with data-theme absent the page
   paints its LIGHT tokens even when the OS is dark. An SVG self-theming on the OS setting would
   then render the cream dark-mode owl on a light card — invisible — for the whole pre-boot window
   and permanently on any page that loads this sheet without shell.js. The file must follow the
   PAGE, not the OS.

   Same filename hook as the mark above and the same collision argument applies: uploads are
   stored under a v4 UUID, which is hex, so no member can name their way into "hudr-owl-avatar".
   The -dark file also contains that substring, which is harmless — it only ever arrives here
   because this rule put it there, and re-matching resolves to the same url. */
img[src*="hudr-owl-avatar"]{content:var(--owl-src,url("/ux/brand/hudr-owl-avatar.svg"))}
.avatar:has(> img[src*="hudr-owl-avatar"]){background:none}
/* The mark is transparent, and must stay that way — the invert above has to produce a white glyph
   ON the dark canvas, not a white glyph inside a black tile. So the circle underneath it must not
   paint anything: .avatar carries background:#6E7781 (plus the .a-* gradients) as the no-photo
   fallback, and that grey would fill every counter of the glyph. Scoped to .avatar, and to a
   DIRECT child, on purpose — .hcmt-av (comments) and .msg-av (chat) are already transparent when
   a photo exists, while .gm-chip / .collab-chip legitimately paint a pill *around* a small avatar
   and must keep it. Specificity here is (0,2,1) — `:has()` adds the weight of its most specific
   argument — so it also beats the (0,2,0) `.avatar.a-blue` gradients if one is ever applied. */
.avatar:has(> img[src*="hudr-mark"]){background:none}
/* ---- @hudr's static "hudr" WORDMARK avatar -----------------------------------------------
   Same file-swap technique as the owl, for the reason documented beside --wordmark-src in the
   dark token block: the mark is drawn in brand ink (#1d1f21), not pure #000, so filter:invert()
   would miss the #f2efe7 paper colour. The light file is the fallback because --wordmark-src is
   only declared in the dark block — undefined elsewhere, so the var() falls back, exactly like
   --owl-src above. Same UUID-filename collision argument, same transparent-disc argument: the
   wordmark is stroked ink on nothing, so the .avatar fallback grey must not paint behind it. */
img[src*="hudr-wordmark"]{content:var(--wordmark-src,url("/ux/brand/hudr-wordmark.svg"))}
.avatar:has(> img[src*="hudr-wordmark"]){background:none}

/* The "built in <country>" flag badge moved to public/ux/flag-badge.css — it is needed on the
   logged-out auth pages too, and those never load this sheet. One file, every surface. */
