/* ═══════════════════════════════════════════
   JETONOMY CSS DESIGN SYSTEM
   Theme-adaptive via CSS Custom Properties
   Inherits from WP theme.json, falls back to neutral defaults
   ═══════════════════════════════════════════ */


/* Design tokens (--jt-*) are NOT declared here. They live in
   assets/css/jetonomy-tokens.css, registered as handle `jetonomy-tokens`,
   which this stylesheet declares as a dependency — so it is always loaded and
   parsed first. They were moved out because blocks render on pages this file
   never loads on; see the header of that file for the full rationale.
   Add a new token THERE, on :root. Never re-declare a --jt-* token in this
   file or in any component rule: a component that re-declares a token stops
   inheriting dark mode and has to be hand-maintained forever. Components
   consume tokens; to make one stand out, mix a percentage of an existing
   token (color-mix) rather than pinning a literal value. */

  /* Dark-mode heading contrast override. Reign (and some other BuddyX-
     adjacent themes) ship an inline customizer rule that hardcodes
     `h1..h6 { color: #20262e }` and does NOT adapt when we flip the body
     background to dark. Without this override the space title, single-
     post h1, category headers, and every other plugin heading render
     near-black on near-black in dark mode. Scoped to `.jt-app` so we only
     override headings inside plugin surfaces; theme pages outside our app
     keep whatever the theme wants. */
  .jt-dark .jt-app h1,
  .jt-dark .jt-app h2,
  .jt-dark .jt-app h3,
  .jt-dark .jt-app h4,
  .jt-dark .jt-app h5,
  .jt-dark .jt-app h6 {
    color: var(--jt-text);
  }

  /* ── Font size scaling (A / A+ / A++) ─────────────────────────────────────
     Uses the same data-bn-font-scale attribute and bn_font_scale localStorage
     key as BuddyNext so the preference is shared across both plugins.
     Since .jt-app uses rem-based type scale, html font-size % scales everything
     uniformly — just like browser zoom. */
  html[data-bn-font-scale="110"] { font-size: 110%; }
  html[data-bn-font-scale="120"] { font-size: 120%; }

  /* Reset within app — lock down ALL typography so theme styles never leak in.
     Follows BuddyNext type scale: base 15px, headings 1.375→0.8125rem.
     Works identically with or without BuddyNext active. */
  .jt-app { font-family: var(--jt-font); font-size: 0.9375rem; line-height: 1.65; color: var(--jt-text); }
  .jt-app *, .jt-app *::before, .jt-app *::after { box-sizing: border-box; }

  /* Heading reset — override BuddyX/WordPress heading styles within the app */
  .jt-app h1 { font-family: var(--jt-font-heading); font-size: 1.375rem; font-weight: 700; line-height: 1.3; margin: 0; }
  .jt-app h2 { font-family: var(--jt-font-heading); font-size: 1.25rem; font-weight: 700; line-height: 1.3; margin: 0; }
  .jt-app h3 { font-family: var(--jt-font-heading); font-size: 1.125rem; font-weight: 700; line-height: 1.35; margin: 0; }
  .jt-app h4 { font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--jt-text-tertiary); margin: 0; }
  .jt-app h5 { font-size: 0.8125rem; font-weight: 600; margin: 0; }
  .jt-app h6 { font-size: 0.75rem; font-weight: 600; margin: 0; }
  .jt-app p { margin: 0 0 0.75em; }
  .jt-app button, .jt-app input, .jt-app select, .jt-app textarea { font-family: var(--jt-font); font-size: inherit; }
  .jt-app button:hover, .jt-app button:focus { background: inherit; color: inherit; }

  /* Links inherit text color by default (Notion-style).
     Only explicit .jt-link or interactive elements use accent color. */
  .jt-app a { color: inherit; text-decoration: none; transition: color var(--jt-dur); }
  .jt-app a:hover { color: var(--jt-text-secondary); }

  /* ── Community Sub-Navigation (below theme header) ── */
  .jt-community-nav { border-bottom: 1px solid var(--jt-border); margin-bottom: 24px; }
  .jt-community-nav-inner { display: flex; align-items: center; justify-content: space-between; }
  .jt-community-nav-links { display: flex; gap: 2px; }
  .jt-community-nav-links a { display: inline-flex; align-items: center; gap: 6px; font-size: 0.9375rem; color: var(--jt-text-secondary); font-weight: 500; padding: 10px 14px; border-bottom: 2px solid transparent; transition: all var(--jt-dur) var(--jt-ease); }
  .jt-community-nav-links a svg { flex-shrink: 0; }
  .jt-community-nav-links a:hover { color: var(--jt-text); border-bottom-color: var(--jt-border-strong); }
  .jt-community-nav-links a.active { color: var(--jt-text); border-bottom-color: var(--jt-accent); font-weight: 600; }
  .jt-community-nav-actions { display: flex; align-items: center; gap: 8px; }
  .jt-community-nav-notif { background: none; color: var(--jt-text-secondary); display: flex; align-items: center; gap: 4px; padding: 8px; border: none; border-radius: var(--jt-radius-full); transition: all var(--jt-dur); position: relative; }
  .jt-community-nav-notif:hover { color: var(--jt-text); background: var(--jt-bg-muted); border: none; }
  .jt-community-nav-badge { background: var(--jt-danger); color: var(--jt-white); font-size: 0.75rem; font-weight: 700; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; }

  /* Legacy header classes kept for backward compatibility */
  .jt-header { display: none; } /* Hidden — theme header handles this */

  /* ── Avatar ── */
  .jt-avatar { border-radius: 50%; background: var(--jt-bg-muted); color: var(--jt-text-secondary); display: flex; align-items: center; justify-content: center; font-weight: 600; font-family: var(--jt-font); flex-shrink: 0; }
  .jt-avatar-sm { width: 30px; height: 30px; font-size: 0.75em; }
  .jt-avatar-md { width: 36px; height: 36px; font-size: 0.875em; }
  .jt-avatar-lg { width: 48px; height: 48px; font-size: 1.125em; }
  .jt-avatar img, img.jt-avatar { border-radius: 50%; object-fit: cover; }

  /* ── Online status indicator ── */
  .jt-avatar-wrap { position: relative; display: inline-block; }
  /* Online indicator sits at the top-right of the avatar image. The wrap
     may also contain the user's name link (reply-card, user profile), so
     the wrap's bounding box extends far to the right and downward. We
     can't anchor to the wrap's bottom-right — that lands under the name
     text. Instead, the avatar image is always the first inline child and
     starts at the wrap's top-left, so we position the dot using `top`
     and a `left` offset equal to the avatar size minus the dot size.
     Per-size rules via :has() keep the dot aligned across sm/md/lg. */
  .jt-avatar-wrap.is-online::after {
    content: '';
    position: absolute;
    top: -2px;
    left: 18px;
    width: 10px;
    height: 10px;
    background: var(--jt-success);
    border-radius: var(--jt-radius-full);
    border: 2px solid var(--jt-bg);
    box-sizing: content-box;
    pointer-events: none;
    z-index: 1;
  }
  .jt-avatar-wrap.is-online:has(.jt-avatar-md)::after { left: 24px; }
  .jt-avatar-wrap.is-online:has(.jt-avatar-lg)::after { left: 36px; }
  /* The user-profile header avatar uses .jt-profile-av (64x64 with a 3px
     border). Offset matches the visible rim: 64px + 3px border = 67px
     outer edge, dot is 14px wide → left = 67 - 14 + 2 = 55; top sits
     level with the border. */
  .jt-avatar-wrap.is-online:has(.jt-profile-av)::after { top: 1px; left: 55px; }

  /* ── Layout ── */
  /* Force the app wrapper to fill the theme's container — prevents flex/grid parents from shrinking it. */
  #jetonomy-app { width: 100%; min-width: 0; flex: 1 1 100%; }
  /* max-width is set dynamically via --jt-container-width (injected by PHP from theme settings).
     Detection order: Jetonomy setting → theme.json wideSize → $content_width → 1200px fallback. */
  .jt-container { max-width: var(--jt-container-width, 1200px); margin: 0 auto; padding: 12px 24px 24px; }
  .jt-two-col { display: grid !important; grid-template-columns: 1fr 280px; gap: 24px; align-items: start; }
  .jt-two-col > main { min-width: 0; }
  .jt-two-col > .jt-sidebar,
  .jt-two-col > aside { position: sticky; top: 24px; align-self: start; }

  /* ── Theme compatibility ── */
  /* Hide the theme's page title/header on Jetonomy pages — themes inject titles
     like "Recent Posts" or "Blog" because WP sees the community as a blog page. */
  body.jt-page > .page-header,
  body.jt-page .site-main > .page-header,
  body.jt-page .site-content > .page-header,
  body.jt-page #content > .page-header { display: none; }

  /* ── Breadcrumb ── */
  .jt-crumb { display: flex; align-items: center; gap: 6px; font-size: 0.8125rem; color: var(--jt-text-secondary); margin-bottom: 20px; }
  .jt-crumb a { color: var(--jt-text-tertiary); }
  .jt-crumb a:hover { color: var(--jt-text-secondary); }

  /* ── Space cover image ── */
  .jt-space-cover {
    position: relative;
    display: flex;
    align-items: flex-end;
    min-height: 180px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    border-radius: var(--jt-radius-lg) var(--jt-radius-lg) 0 0;
    overflow: hidden;
    margin-bottom: 0;
  }
  /* Tonal cover fallback — a space with no cover image still gets a branded
     banner (matches BuddyNext's gradient space cover). */
  .jt-space-cover--tonal {
    background-image: linear-gradient(135deg, color-mix(in srgb, var(--jt-accent) 22%, var(--jt-surface)), color-mix(in srgb, var(--jt-accent) 7%, var(--jt-surface)));
  }
  /* Scrim — only on photo covers, so the overlaid avatar + title stay legible. */
  .jt-space-cover--image::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.15) 38%, transparent 70%);
    pointer-events: none;
  }
  /* Identity overlaid on the cover's lower edge: avatar tile + title — the
     BuddyNext space-header pattern (identity lives ON the cover, not below it). */
  .jt-space-cover__identity {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    padding: var(--jt-space-4);
  }
  .jt-space-cover__title {
    margin: 0;
    font-family: var(--jt-font-heading);
    font-weight: 700;
    font-size: var(--jt-text-2xl);
    letter-spacing: -0.02em;
    line-height: 1.2;
    color: var(--jt-text);
  }
  .jt-space-cover--image .jt-space-cover__title { color: #fff; }
  /* Space avatar tile — surface fill, ring, soft shadow; reads on tonal + photo. */
  .jt-space-emoji { display: inline-flex; align-items: center; justify-content: center; width: 64px; height: 64px; border-radius: var(--jt-radius-lg); background: var(--jt-surface); border: 3px solid var(--jt-surface); box-shadow: var(--jt-shadow-sm); color: var(--jt-accent); flex-shrink: 0; }
  .jt-space-emoji svg { width: 32px; height: 32px; }
  .jt-space-emoji.dashicons { font-size: 30px; width: auto; height: auto; }

  @media (max-width: 640px) {
  .jt-crop-dialog { max-width: none; padding: var(--jt-space-lg, 16px); }

    .jt-space-cover { min-height: 150px; border-radius: 0; margin-left: -16px; margin-right: -16px; }
    .jt-space-cover__identity { flex-direction: column; align-items: center; text-align: center; gap: 8px; }
  }

  /* ── Space header (below the cover) — description + stats + follow ── */
  .jt-space-head { display: flex; align-items: flex-start; gap: 16px; padding: var(--jt-space-4) 4px 20px; margin-bottom: 16px; border-bottom: 1px solid var(--jt-border); }
  .jt-space-head__meta { flex: 1 1 auto; min-width: 0; }
  .jt-space-desc { color: var(--jt-text-secondary); font-size: 0.9375rem; line-height: 1.5; margin-top: 6px; }
  .jt-space-nums { display: flex; gap: 20px; margin-left: auto; }
  .jt-num { text-align: center; }
  .jt-num-val { font-family: var(--jt-font-mono); font-size: 1.25rem; font-weight: 700; }
  .jt-num-lbl { font-size: 0.8125rem; color: var(--jt-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em; }

  /* ── Toolbar ── */
  .jt-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
  .jt-pills { display: flex; gap: 2px; background: var(--jt-bg-muted); border-radius: var(--jt-radius-full); padding: 3px; }
  .jt-pill { padding: 5px 14px; font-size: 0.875rem; font-weight: 500; color: var(--jt-text-secondary); border-radius: var(--jt-radius-full); transition: all var(--jt-dur) var(--jt-ease); border: none; background: none; cursor: pointer; font-family: var(--jt-font); }
  .jt-pill:hover { color: var(--jt-text); }
  .jt-pill.on, .jt-pill.active { background: var(--jt-bg); color: var(--jt-text); font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,0.08), inset 0 -2px 0 var(--jt-accent); }

  /* ── Buttons ── */
  .jt-app .jt-btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 18px; border-radius: var(--jt-radius-full); font-weight: 600; font-size: 0.9375rem; text-decoration: none; transition: all var(--jt-dur) var(--jt-ease); border: none; cursor: pointer; font-family: var(--jt-font); }
  .jt-app .jt-btn-fill, .jt-app a.jt-btn-fill, .jt-app button.jt-btn-fill { background: var(--jt-accent); color: var(--jt-accent-fg); }
  .jt-app .jt-btn-fill:hover, .jt-app a.jt-btn-fill:hover { background: var(--jt-accent-hover); color: var(--jt-accent-hover-fg); text-decoration: none; }
  .jt-app .jt-btn-ghost, .jt-app a.jt-btn-ghost { color: var(--jt-text-secondary); border: 1.5px solid var(--jt-border-strong); background: var(--jt-bg); }
  .jt-app .jt-btn-ghost:hover, .jt-app a.jt-btn-ghost:hover { background: var(--jt-bg-hover); color: var(--jt-text); border-color: var(--jt-border-strong); text-decoration: none; }

  /* ── Cross-theme <button> insulation (premium, uniform look in any theme) ──
     Aggressive themes paint every bare <button> with their own accent via a
     high-specificity `button:not(...)` chain — BuddyX 5.1's bx-tokens-applied.css
     uses `button:not([class*="wp-"])…` at ~(0,6,1), which outranks the plugin's
     button classes and turns every icon / action / menu / reaction button into a
     solid accent pill (392 buttons repainted on a single thread). The theme rule
     is NOT !important, so a scoped !important reset on .jt-app buttons wins no
     matter the theme's selector specificity. Neutralize the theme paint, then
     re-assert the few plugin buttons that intentionally carry a fill or surface.
     Only <button> needs this; <a class="jt-btn"> is not matched by the theme's
     `button` selector. */
  .jt-app button { background-color: transparent !important; background-image: none !important; color: inherit !important; box-shadow: none !important; border-color: var(--jt-border) !important; }
  /* Re-assert the plugin's own intentional button surfaces/text that the reset
     above (and the theme) would otherwise flatten. Each carries !important to
     beat the blanket; the design tokens keep them theme-adaptive. */
  .jt-app button.jt-btn-fill { background-color: var(--jt-accent) !important; color: var(--jt-accent-fg) !important; }
  .jt-app button.jt-btn-fill:hover, .jt-app button.jt-btn-fill:focus-visible { background-color: var(--jt-accent-hover) !important; color: var(--jt-accent-hover-fg) !important; }
  .jt-app button.jt-btn-ghost { background-color: var(--jt-bg) !important; color: var(--jt-text-secondary) !important; border-color: var(--jt-border-strong) !important; }
  .jt-app button.jt-btn-ghost:hover { background-color: var(--jt-bg-hover) !important; color: var(--jt-text) !important; border-color: var(--jt-border-strong) !important; }
  /* Segmented controls (sort + filter pills, font-scale): active pill keeps its
     raised surface + accent underline. */
  .jt-app button.jt-pill.on, .jt-app button.jt-pill.active,
  .jt-app button.jt-font-scale__btn.active { background-color: var(--jt-bg) !important; color: var(--jt-text) !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08), inset 0 -2px 0 var(--jt-accent) !important; }
  /* Icon/action toolbar hover feedback. */
  .jt-app button.jt-act:hover { background-color: var(--jt-bg-muted) !important; color: var(--jt-text-secondary) !important; }
  /* Dropdown menu items: hover surface + danger text. */
  .jt-app button.jt-more-item:hover { background-color: var(--jt-bg-hover) !important; }
  .jt-app button.jt-more-item--danger { color: var(--jt-danger) !important; }
  .jt-app button.jt-more-item--danger:hover { background-color: var(--jt-danger-light) !important; color: var(--jt-danger) !important; }

  /* ── Topic list ── */
  .jt-topics { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); overflow: hidden; }
  .jt-row { display: grid !important; grid-template-columns: 48px 1fr 80px 80px; gap: 12px; align-items: center; padding: 12px 16px; border-bottom: 1px solid var(--jt-border); transition: background 0.1s; cursor: pointer; }
  .jt-row:last-child { border-bottom: none; }
  .jt-row:hover { background: var(--jt-bg-hover); }
  .jt-row.pinned { background: var(--jt-accent-muted); }
  a.jt-row { text-decoration: none; color: inherit; display: grid; grid-template-columns: 48px 1fr 80px 80px; gap: 12px; align-items: center; }
  a.jt-row:hover { color: inherit; }
  .jt-row-main { min-width: 0; }
  .jt-row-title { font-weight: 600; font-size: 1rem; line-height: 1.4; color: var(--jt-text); }
  /* 1.4.0 C.4: stretched-link pattern. Title <a> is the row's only navigation
     anchor; a ::before pseudo covers the whole row so the row stays clickable
     while child <a> elements (tags) keep their own click target. */
  a.jt-row-title-link { display: block; text-decoration: none; color: inherit; position: static; }
  .jt-row { position: relative; }
  a.jt-row-title-link::before { content: ""; position: absolute; inset: 0; z-index: 0; }
  .jt-row-sub > * { position: relative; z-index: 1; }

  /* 1.4.0 C.7: @mention typeahead dropdown. */
  .jt-mention-dropdown { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); box-shadow: 0 4px 14px rgb(0 0 0 / 0.08); min-width: 240px; max-width: 320px; max-height: 280px; overflow-y: auto; }
  .jt-mention-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; border-block-end: 1px solid var(--jt-border); }
  .jt-mention-row:last-child { border-block-end: none; }
  .jt-mention-row:hover, .jt-mention-row.is-selected { background: var(--jt-bg-hover); }
  .jt-mention-avatar { width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; }
  .jt-mention-name { font-weight: 600; color: var(--jt-text); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .jt-mention-login { color: var(--jt-text-tertiary); font-size: 0.8125rem; }
  .jt-mention-empty { padding: 10px 12px; color: var(--jt-text-tertiary); font-size: 0.875rem; }

  /* 1.4.0 C.5: "New" pill on post cards when the viewer has unread replies. */
  .jt-unread-pill { display: inline-block; margin-inline-start: 8px; font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; padding: 2px 6px; border-radius: var(--jt-radius-full); background: var(--jt-accent); color: var(--jt-accent-fg); vertical-align: middle; }

  /* Front-end forms (G5 edit space, G6 new space). Token-driven so the
     same primitives work for any future REST-backed form. */
  .jt-form { padding: var(--jt-space-4); }
  .jt-form-row { margin-block-end: 16px; }
  .jt-form-row label { display: block; font-weight: 600; margin-block-end: 6px; color: var(--jt-text); }
  .jt-form-row .jt-input { width: 100%; padding: 8px 12px; border: 1px solid var(--jt-border); border-radius: var(--jt-radius); background: var(--jt-bg); color: var(--jt-text); font: inherit; }
  /* Focus ring is owned by the global .jt-input:focus rule (border + box-shadow).
     A second outline here stacked into a double ring — removed. */
  .jt-form-row textarea.jt-input { resize: vertical; }
  .jt-form-help { color: var(--jt-text-tertiary); font-size: 0.8125rem; margin: 6px 0 0; }
  .jt-required { color: var(--jt-danger); }
  .jt-form-actions { display: flex; align-items: center; gap: 12px; margin-block-start: 8px; flex-wrap: wrap; }
  .jt-form-error { margin-block-start: 12px; padding: 10px 12px; border-radius: var(--jt-radius); background: var(--jt-danger-light); color: var(--jt-danger); font-size: 0.875rem; }
  .jt-form-saved { color: var(--jt-success); font-weight: 600; font-size: 0.875rem; }
  @media ( max-width: 640px ) {
    .jt-form { padding: 16px; }
    .jt-form-actions { flex-direction: column; align-items: stretch; }
    .jt-form-actions .jt-btn { width: 100%; text-align: center; }
  }

  /* 1.4.0 G5/G6: cover-image uploader + Lucide icon picker on the
     front-end space-edit and create-space forms. */
  .jt-cover-uploader { display: flex; flex-direction: column; gap: 12px; padding: 12px; border: 1px dashed var(--jt-border); border-radius: var(--jt-radius); }
  .jt-cover-preview { width: 100%; max-width: 480px; aspect-ratio: 16 / 5; border-radius: var(--jt-radius); overflow: hidden; background: var(--jt-bg-subtle); }
  .jt-cover-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .jt-cover-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
  .jt-cover-pick { cursor: pointer; }
  .jt-cover-status { color: var(--jt-text-secondary); font-size: 0.875rem; }

  /* Icon-only picker (1.4.0 G5/G6): 8 cols × 2 visible rows by default,
     extended icons reveal via "Show more" or live search filter.
     Note: rules use `.jt-icon-picker .jt-icon-option` (0,2,0) so they
     beat the generic `.jt-form-row label { display: block }` rule
     (0,1,1) — otherwise cells would render as block, killing the
     square aspect-ratio and the centered icon. */
  .jt-icon-picker-wrap { display: flex; flex-direction: column; gap: 10px; }
  .jt-icon-picker-search { position: relative; }
  .jt-icon-picker-search .jt-input { padding-inline-start: 36px; }
  .jt-icon-picker-search-icon { position: absolute; inset-inline-start: 10px; inset-block-start: 50%; transform: translateY(-50%); color: var(--jt-text-tertiary); pointer-events: none; display: inline-flex; }
  .jt-icon-picker-search-icon svg { width: 16px; height: 16px; }
  .jt-icon-picker { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 8px; }
  .jt-icon-picker .jt-icon-option { position: relative; display: flex; align-items: center; justify-content: center; aspect-ratio: 1 / 1; margin: 0; padding: 0; border: 1px solid var(--jt-border); border-radius: var(--jt-radius); cursor: pointer; transition: border-color 0.15s ease, background 0.15s ease; background: var(--jt-bg); font-weight: 400; }
  .jt-icon-picker .jt-icon-option:hover { border-color: var(--jt-accent); background: var(--jt-bg-hover); }
  .jt-icon-picker .jt-icon-option input[type=radio] { position: absolute; opacity: 0; width: 0; height: 0; }
  .jt-icon-picker .jt-icon-option.is-selected { border-color: var(--jt-accent); background: var(--jt-accent-muted); }
  .jt-icon-picker .jt-icon-option[hidden] { display: none; }
  .jt-icon-option-svg { display: inline-flex; align-items: center; justify-content: center; color: var(--jt-accent); }
  .jt-icon-option-svg svg { width: 22px; height: 22px; }
  .jt-icon-picker-more { align-self: flex-start; font-size: 0.875rem; }
  .jt-icon-picker-empty { color: var(--jt-text-secondary); font-size: 0.875rem; padding: 8px 4px; grid-column: 1 / -1; margin: 0; }

  /* Posts-per-page narrow input + prefix editor (G5 settings). */
  .jt-input-narrow { max-width: 120px; }
  .jt-prefixes-row .jt-prefix-toggle { display: flex; align-items: center; gap: 8px; font-weight: 600; }
  .jt-prefix-config { margin-block-start: 12px; padding: 12px; border: 1px dashed var(--jt-border); border-radius: var(--jt-radius); }
  .jt-prefix-list { display: flex; flex-direction: column; gap: 8px; margin-block-end: 12px; }
  .jt-prefix-row { display: flex; align-items: center; gap: 8px; }
  .jt-prefix-row .jt-input { flex: 1 1 auto; }
  .jt-prefix-color { width: 36px; height: 36px; padding: 0; border: 1px solid var(--jt-border); border-radius: var(--jt-radius); cursor: pointer; flex-shrink: 0; }
  .jt-prefix-remove { padding: 6px 12px; min-width: auto; flex-shrink: 0; font-size: 1.125rem; line-height: 1; }
  @media ( max-width: 640px ) {
    .jt-icon-picker { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    .jt-cover-preview { aspect-ratio: 16 / 6; }
  }

  /* Page-head pattern (1.4.0): h1 + muted subtitle, generous gap below. */
  .jt-page-head { margin-block-end: 28px; }
  .jt-page-head .jt-page-title { margin: 0 0 4px; }
  .jt-page-head .jt-page-subtitle { margin: 0; color: var(--jt-text-secondary); font-size: 0.9375rem; }

  /* My Spaces landing (G7). Two stacked sections, card per space. */
  .jt-my-spaces-section { margin-block-end: 36px; }
  .jt-my-spaces-section:last-child { margin-block-end: 0; }
  .jt-space-card-icon { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; flex-shrink: 0; color: var(--jt-accent); background: var(--jt-accent-muted); border-radius: var(--jt-radius); }
  .jt-space-card-icon svg { width: 22px; height: 22px; }
  .jt-space-card-icon.dashicons { font-size: 22px; }
  .jt-space-list { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
  .jt-space-card { background: var(--jt-surface); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); box-shadow: var(--jt-shadow-sm); transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease; display: flex; flex-direction: column; }
  .jt-space-card:hover { border-color: var(--jt-accent); box-shadow: var(--jt-shadow-md); }
  .jt-space-card-link { display: flex; flex-direction: column; gap: 10px; padding: 16px 16px 14px; text-decoration: none; color: inherit; flex: 1 1 auto; }
  .jt-space-card-link:hover { color: inherit; }
  .jt-space-card-head { display: flex; align-items: flex-start; gap: 12px; }
  .jt-space-card-titlewrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 1 1 auto; min-width: 0; }
  .jt-space-card-title { font-size: 1rem; font-weight: 700; color: var(--jt-text); margin: 0; line-height: 1.3; }
  .jt-space-card-desc { color: var(--jt-text-secondary); font-size: 0.875rem; line-height: 1.5; margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .jt-space-card-meta { color: var(--jt-text-tertiary); font-size: 0.8125rem; margin: 0; }

  /* Footer action row — pill-shaped buttons divided from the card body
     by a top border. Each action is a real ghost button so customers
     read them as interactive, not as muted text. */
  .jt-space-card-actions { display: flex; gap: 6px; padding: 10px 14px; border-block-start: 1px solid var(--jt-border); background: var(--jt-bg-subtle); flex-wrap: wrap; }
  .jt-space-card-action { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border-radius: var(--jt-radius-sm, 6px); background: var(--jt-bg); border: 1px solid var(--jt-border); color: var(--jt-text); font-size: 0.8125rem; font-weight: 500; text-decoration: none; transition: border-color 0.1s ease, color 0.1s ease, background 0.1s ease; }
  .jt-space-card-action:hover { border-color: var(--jt-accent); color: var(--jt-accent); background: var(--jt-accent-muted); }
  .jt-space-card-action svg { flex-shrink: 0; }
  .jt-empty-cta { margin-block-start: 16px; text-align: center; }
  @media ( max-width: 640px ) {
    .jt-space-list { grid-template-columns: 1fr; }
  }

  /* Tag-page + future paginated lists. */
  .jt-pagination { display: flex; align-items: center; justify-content: center; gap: 16px; margin-block-start: 24px; padding-block: 16px; }
  .jt-pagination-link { color: var(--jt-accent); text-decoration: none; font-weight: 600; padding: 8px 14px; border-radius: var(--jt-radius); transition: background 0.1s; }
  .jt-pagination-link:hover { background: var(--jt-bg-hover); }
  .jt-pagination-status { color: var(--jt-text-secondary); font-size: 0.875rem; }
  @media ( max-width: 640px ) {
    .jt-pagination { gap: 8px; flex-wrap: wrap; }
    .jt-pagination-link { padding: 8px 10px; font-size: 0.875rem; }
  }
  .jt-row:hover .jt-row-title { color: var(--jt-text-secondary); }
  .jt-row-sub { font-size: 0.8125rem; color: var(--jt-text-tertiary); margin-top: 3px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .jt-row-stat { text-align: center; }
  .jt-row-stat-n { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; }
  .jt-row-stat-l { font-size: 0.9375rem; color: var(--jt-text-secondary); }
  /* Drafts-tab rows have a 2-column shape (title | actions), not the 4-column
     topic-listing grid. Without this the title lands in the 48px icon column and
     wraps one word per line, with a huge gap before the meta column (Basecamp
     drafts-tab alignment). The `.jt-row.jt-row--draft` (0,2,0) specificity beats
     both the base and the mobile `.jt-row` (0,1,0) templates at every breakpoint,
     and the stat rule (0,3,0) overrides the mobile `.jt-row-stat:last-child`
     display:none so the time + Publish-now button stay visible. */
  .jt-row.jt-row--draft { grid-template-columns: 1fr auto; }
  .jt-row.jt-row--draft .jt-row-stat { display: flex; align-items: center; gap: var(--jt-space-3); text-align: end; }

  /* ── Vote buttons ── */
  .jt-votes { display: flex; flex-direction: column; align-items: center; gap: 1px; position: relative; z-index: 1; }
  .jt-v-btn { width: 26px; height: 20px; display: flex; align-items: center; justify-content: center; color: var(--jt-text-secondary); font-size: 0.8125rem; border-radius: 3px; transition: all 0.1s; border: none; background: none; cursor: pointer; }
  .jt-v-btn:hover { color: var(--jt-text-secondary); background: var(--jt-accent-muted); }
  .jt-v-btn.jt-voted, .jt-v-btn.voted { color: var(--jt-accent); }
  /* Keep the chevron from collapsing to 0 width as a flex child (Chromium SVG-in-flex quirk). */
  .jt-v-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
  .jt-act.voted { color: var(--jt-accent); }
  /* Logged-out vote control is a "Log in to vote" link (1.5.0) — match the
     button look (no underline, inherited colour, pointer). */
  .jt-act.jt-act-login { text-decoration: none; color: var(--jt-text-secondary); cursor: pointer; }
  .jt-act.jt-act-login:hover { color: var(--jt-accent); }
  /* Moderator "open reports" indicator: danger-tinted so flagged content reads
     as needing attention; the count badge sits on the flag icon. */
  .jt-act.jt-flagged-indicator { color: var(--jt-danger); text-decoration: none; }
  .jt-act.jt-flagged-indicator:hover { color: var(--jt-danger); background: var(--jt-danger-light); }
  .jt-flagged-count { font-weight: 700; font-family: var(--jt-font-mono); font-size: 0.8125rem; }
  .jt-v-num { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 700; }

  /* Vote cluster — visual grouping for up + down so they read as
     equal-weight peers in the action bar. End-user goal: both
     directions feel equally available, no friction asymmetry.
     The inner buttons keep the existing .jt-act styling so JS
     bindings + hover affordances stay unchanged. */
  .jt-vote-cluster { display: inline-flex; align-items: center; gap: 2px; }


  /* ── Tags ── */
  .jt-tag { font-size: 0.9375rem; font-weight: 500; padding: 1px 7px; border-radius: var(--jt-radius-full); background: var(--jt-bg-muted); color: var(--jt-text-secondary); display: inline-block; }
  .jt-tag-qa { background: var(--jt-success-light); color: var(--jt-success); }
  .jt-tag-ideas { background: var(--jt-warn-light); color: var(--jt-warn); }

  /* ── Inline mentions & hashtag links ── */
  .jt-mention { color: var(--jt-accent); font-weight: 600; }
  .jt-mention:hover { text-decoration: underline; }
  .jt-tag-link { color: var(--jt-text-tertiary); text-decoration: none; font-size: 0.875rem; }
  .jt-tag-link:hover { color: var(--jt-accent); text-decoration: underline; }

  /* Post-body tag row — tags moved out of byline (1.4.1 byline cleanup) */
  /* Tags are a sibling of .jt-post-body, so they don't inherit its horizontal
     padding — match it (space-6 desktop, space-4 mobile) so hashtags align with
     the body text column instead of sitting flush against the card edge. */
  .jt-post-tags { display: flex; flex-wrap: wrap; gap: var(--jt-space-3); margin-block: var(--jt-space-5); padding-block: 0; padding-inline: var(--jt-space-6); }

  /* ── Trust level badge ── */
  .jt-tl { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; font-size: 0.6875rem; font-weight: 700; color: var(--jt-white); font-family: var(--jt-font-mono); }
  .jt-tl[data-jt-tl="0"] { background: var(--jt-tl0); }
  .jt-tl[data-jt-tl="1"] { background: var(--jt-tl1); }
  .jt-tl[data-jt-tl="2"] { background: var(--jt-tl2); }
  .jt-tl[data-jt-tl="3"] { background: var(--jt-tl3); }
  .jt-tl[data-jt-tl="4"] { background: var(--jt-tl4); }
  .jt-tl[data-jt-tl="5"] { background: var(--jt-tl5); }

  /* ── Post ── */
  /* Note: we dropped `overflow: hidden` from this rule — it was clipping
     the three-dot action dropdown to a thin sliver under the action bar
     (Basecamp 9803818273). Border-radius clipping for the rare
     first-child (cover image) is enforced below via a scoped rule that
     only targets direct children inside the rounded top-left/right
     corners, so we don't pay the clipping tax on the whole card. */
  .jt-post { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); margin-bottom: 20px; }
  .jt-post > *:first-child { border-top-left-radius: var(--jt-radius-lg); border-top-right-radius: var(--jt-radius-lg); }
  .jt-post > *:last-child { border-bottom-left-radius: var(--jt-radius-lg); border-bottom-right-radius: var(--jt-radius-lg); }
  .jt-post-head { display: flex; flex-direction: column; gap: var(--jt-space-4); padding: var(--jt-space-5); border-bottom: 1px solid var(--jt-border); position: relative; }
  .jt-post-title-row { display: flex; align-items: center; gap: var(--jt-space-4); }
  .jt-post-title-row > h1 { flex: 1 1 auto; min-width: 0; }
  .jt-post-title-row > .jt-btn-sm { flex: 0 0 auto; }
  /* When the post is untitled (feed-space) the Follow button lives on the
     meta row instead of a dedicated title row. Push it to the trailing
     edge so the row reads "avatar · name · time · [badges] · Follow". */
  .jt-meta .jt-meta-follow { margin-inline-start: auto; }
  .jt-post-head h1 { font-family: var(--jt-font-heading); font-weight: 700; letter-spacing: -0.02em; line-height: 1.25; margin: 0; }
  /* Meta row — author + trust + time + tags. Flex-wraps atomically
     so individual items never break mid-word on narrow viewports.
     See docs/DESIGN-SYSTEM.md §9 "Meta line". */
  .jt-meta { display: flex; align-items: center; flex-wrap: wrap; gap: var(--jt-space-2) var(--jt-space-3); color: var(--jt-text-secondary); }
  .jt-meta > * { white-space: nowrap; flex-shrink: 0; }
  .jt-meta > a { min-height: var(--jt-space-8); display: inline-flex; align-items: center; }
  .jt-meta strong { color: var(--jt-text); }
  .jt-post-body { padding: var(--jt-space-6); font-size: 1rem; line-height: 1.8; }
  .jt-post-body p { margin: 0 0 var(--jt-space-3); }
  /* Prose element rules are shared by the post body AND the reply body so every
     content type (lists, code, blockquotes, long words) aligns and wraps the
     same way in both — no per-container drift. Long unbroken tokens (URLs, hashes)
     break instead of overflowing the card. img/table/iframe overflow rules are
     grouped for both containers further down. */
  .jt-post-body, .jt-reply-body { overflow-wrap: break-word; }
  /* Lists inherit a zeroed padding from the global reset, so outside markers
     overflow left toward the card edge. Restore a marker gutter so 1./2./bullets
     sit inside the reading column, aligned under the body text. */
  .jt-post-body ol, .jt-post-body ul,
  .jt-reply-body ol, .jt-reply-body ul { margin: 0 0 var(--jt-space-3); padding-inline-start: var(--jt-space-6); }
  .jt-post-body li, .jt-reply-body li { margin-block: var(--jt-space-1); }
  .jt-post-body code, .jt-reply-body code { background: var(--jt-bg-muted); padding: 2px 6px; border-radius: 4px; font-family: var(--jt-font-mono); }
  /* code inside a code block must not also get the inline-code chip background. */
  .jt-post-body pre code, .jt-reply-body pre code { background: none; padding: 0; border-radius: 0; }
  .jt-post-body pre, .jt-reply-body pre { background: var(--jt-code-bg); color: var(--jt-code-fg); padding: var(--jt-space-4); border-radius: var(--jt-radius); overflow-x: auto; font-family: var(--jt-font-mono); line-height: 1.6; margin: 0 0 var(--jt-space-3); }
  .jt-post-body blockquote, .jt-reply-body blockquote { margin: 0 0 var(--jt-space-3); padding-inline-start: var(--jt-space-4); border-inline-start: 3px solid var(--jt-border); color: var(--jt-text-secondary); }
  .jt-post-foot { display: flex; align-items: center; gap: var(--jt-space-3); padding-block: var(--jt-space-3); padding-inline: var(--jt-space-4); border-top: 1px solid var(--jt-border); }

  /* ── Action buttons ── */
  .jt-act { display: inline-flex; align-items: center; gap: var(--jt-space-2); padding-block: var(--jt-space-2); padding-inline: var(--jt-space-3); border-radius: var(--jt-radius-full); font-size: 0.8125rem; color: var(--jt-text-tertiary); font-weight: 500; transition: all 0.1s; border: none; background: none; cursor: pointer; font-family: var(--jt-font); min-block-size: 36px; }
  /* Touch devices: bump to ≥44px so a thumb-tap doesn't slip onto the
     wrong action. Hover-capable devices (mouse) stay at 36px because
     pointer precision is finer there. */
  @media (hover: none) {
    .jt-act { min-block-size: 44px; padding-inline: var(--jt-space-4); }
    /* Small buttons have no min-height for mouse density; on touch they
       need the full tap target so per-row controls (e.g. the member "Ban
       from space" button) are not a slip hazard. Matches the regular
       .jt-btn mobile target. Applies everywhere .jt-btn-sm is used. */
    .jt-app .jt-btn-sm { min-height: var(--jt-tap); }
    /* Stacked list vote chevrons are 20px tall for mouse density — far too
       small to thumb-tap, and up/down sit right on top of each other. Give
       each the full tap token on touch so voting from a listing is reliable. */
    .jt-v-btn { height: var(--jt-tap); }
  }
  .jt-act:hover { background: var(--jt-bg-muted); color: var(--jt-text-secondary); border: none; }
  .jt-act .n { font-family: var(--jt-font-mono); font-weight: 700; }

  /* ── Replies ── */
  .jt-replies-head { display: flex; align-items: center; justify-content: space-between; margin: 0 0 var(--jt-space-4); }
  .jt-replies-head h2, .jt-replies-head h3 { font-family: var(--jt-font-heading); font-weight: 700; display: flex; align-items: center; gap: var(--jt-space-2); margin: 0; }
  .jt-count-pill { font-family: var(--jt-font-mono); font-size: 0.75rem; font-weight: 600; line-height: 1.4; padding: 2px var(--jt-space-2); border-radius: var(--jt-radius-full); background: var(--jt-accent-light); color: var(--jt-accent); min-width: 1.75em; text-align: center; }

  /* Reply card: must NOT clip overflow, otherwise the three-dot menu dropdown
     (absolute-positioned on the actions row) gets hidden the moment it extends
     past the card boundary. Border-radius is applied to the direct children
     (head + body + footer) instead of the wrapper so corners still round. */
  .jt-reply { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); margin-bottom: var(--jt-space-3); transition: border-color var(--jt-dur); }
  .jt-reply:hover { border-color: var(--jt-border-strong); }
  .jt-reply.accepted { border-color: var(--jt-success); border-width: 2px; }
  /* Deep-linked reply (#reply-{id} from a notification / email / JSON-LD).
     :target rather than JS: the browser already scrolls to the fragment on
     load AND re-evaluates :target on hashchange, so the whole feature needs
     no script — nothing to re-init after a client-side navigation, nothing to
     break when JS is off (crawlers, email webviews). scroll-margin-top keeps
     the card clear of the sticky header it would otherwise land under. */
  .jt-reply:target { border-color: var(--jt-accent); border-width: 2px; scroll-margin-top: var(--jt-scroll-offset); }
  .jt-reply:target > .jt-reply-head { background: var(--jt-accent-light); background: color-mix(in srgb, var(--jt-accent) 12%, transparent); }
  /* Translucent green tint (derived from --jt-success) instead of an opaque
     light token, so the accepted-answer header reads correctly in light AND
     dark and never paints a light block on a dark card. Hex token first as the
     no-color-mix fallback. Matches the .jt-accepted-callout treatment. */
  .jt-reply.accepted .jt-reply-head { background: var(--jt-success-light); background: color-mix(in srgb, var(--jt-success) 12%, transparent); }
  .jt-reply > :first-child { border-top-left-radius: var(--jt-radius-lg); border-top-right-radius: var(--jt-radius-lg); }
  .jt-reply > :last-child { border-bottom-left-radius: var(--jt-radius-lg); border-bottom-right-radius: var(--jt-radius-lg); }
  .jt-reply-head { display: flex; align-items: center; flex-wrap: wrap; row-gap: var(--jt-space-1); gap: var(--jt-space-2); padding: var(--jt-space-3) var(--jt-space-4); background: var(--jt-bg-subtle); }
  /* Anonymous / unknown author: center the silhouette icon and keep it from
     collapsing to 0 width as a flex child. */
  .jt-avatar-anon { color: var(--jt-text-tertiary); }
  .jt-avatar-anon svg { flex-shrink: 0; }
  .jt-reply-body { padding: 16px 18px; font-size: 1rem; line-height: 1.75; }
  .jt-reply-body p { margin: 0 0 10px; }
  .jt-reply-foot { display: flex; align-items: center; gap: var(--jt-space-3); padding-block-start: var(--jt-space-2); padding-block-end: var(--jt-space-3); padding-inline: var(--jt-space-5); }
  .jt-accepted-tag { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-size: 0.8125rem; font-weight: 600; color: var(--jt-success); white-space: nowrap; }
  .jt-accepted-tag svg { flex-shrink: 0; }

  /* Blocked-author tombstone (reply-card.php). Node is kept so nested,
     non-blocked replies still attach — only this row's content is hidden. */
  .jt-reply-blocked { border-style: dashed; }
  .jt-reply-tombstone { display: flex; align-items: center; flex-wrap: wrap; gap: var(--jt-space-2); padding: var(--jt-space-3) var(--jt-space-4); color: var(--jt-text-tertiary); font-size: 0.9375rem; font-style: italic; }
  .jt-reply-tombstone svg { flex-shrink: 0; }
  .jt-unblock-btn { margin-inline-start: auto; font-style: normal; font-size: 0.8125rem; font-weight: 600; color: var(--jt-accent); background: none; border: none; cursor: pointer; padding: 2px var(--jt-space-2); border-radius: var(--jt-radius-sm); }
  .jt-unblock-btn:hover, .jt-unblock-btn:focus-visible { background: var(--jt-accent-light); }

  /* Blocked-author tombstone — full single-post view (single-post.php). Deep
     links to a blocked author's post land here instead of the full content. */
  .jt-post-blocked-tombstone { display: flex; flex-direction: column; align-items: center; gap: var(--jt-space-3); padding: var(--jt-space-6, 32px) var(--jt-space-4); text-align: center; color: var(--jt-text-tertiary); border: 1px dashed var(--jt-border); border-radius: var(--jt-radius-lg); }
  .jt-post-blocked-tombstone p { margin: 0; font-size: 1rem; }

  /* Threaded reply nesting — up to 3 levels */
  .jt-nested { margin-left: 24px; position: relative; }
  .jt-nested::before { content: ''; position: absolute; left: -12px; top: 0; bottom: 0; width: 2px; background: var(--jt-border); border-radius: 1px; }
  .jt-nested-1 { margin-left: 24px; }
  .jt-nested-2 { margin-left: 24px; }
  .jt-nested-3 { margin-left: 24px; }

  /* Gap loader — in-between divider */
  .jt-load-gap { text-align: center; padding: 16px 0; position: relative; }
  .jt-load-gap::before { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: var(--jt-border); }
  .jt-load-gap-btn { position: relative; z-index: 1; background: var(--jt-bg) !important; padding: 8px 20px; border-radius: var(--jt-radius-full); }

  /* Reply-to indicator */
  .jt-replying-to { font-size: 0.9375rem; color: var(--jt-text-tertiary); padding: 8px 14px; border-left: 3px solid var(--jt-accent); background: var(--jt-bg-subtle); margin-bottom: 8px; border-radius: 0 var(--jt-radius) var(--jt-radius) 0; display: flex; align-items: center; justify-content: space-between; }
  .jt-replying-to-cancel { cursor: pointer; color: var(--jt-text-tertiary); border: none; background: none; padding: 4px; }

  /* ── Composer ── */
  .jt-editor { border: 1.5px solid var(--jt-border-strong); border-radius: var(--jt-radius-lg); overflow: hidden; background: var(--jt-bg); transition: border-color 0.2s; }
  .jt-editor:focus-within { border-color: var(--jt-accent); box-shadow: 0 0 0 3px var(--jt-accent-muted); }
  .jt-editor-bar { display: flex; gap: 2px; padding: 6px 10px; border-bottom: 1px solid var(--jt-border); background: var(--jt-bg-subtle); position: relative; }
  .jt-editor-bar button { min-width: 32px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: var(--jt-radius-sm); color: var(--jt-text-secondary); font-size: 1rem; transition: all 0.1s; border: none; background: none; cursor: pointer; padding: 0; }
  .jt-editor-bar button:hover, .jt-editor-bar button:focus { background: var(--jt-bg); color: var(--jt-text); border: none; }
  .jt-editor-body { padding: 16px; min-height: 120px; color: var(--jt-text); outline: none; font-family: var(--jt-font); font-size: 1rem; line-height: 1.75; }
  .jt-editor-body:focus-visible { outline: 2px solid var(--jt-accent); outline-offset: -2px; border-radius: var(--jt-radius-sm); }
  .jt-editor-body:empty::before { content: attr(data-placeholder); color: var(--jt-text-secondary); }
  .jt-editor-foot { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-top: 1px solid var(--jt-border); }
  .jt-editor-hint { font-size: 0.9375rem; color: var(--jt-text-tertiary); font-family: var(--jt-font-mono); }

  /* ── Card (sidebar) ── */
  .jt-card { background: var(--jt-surface); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); box-shadow: var(--jt-shadow-sm); padding-block: var(--jt-space-5); padding-inline: var(--jt-space-5); }
  @media (max-width: 640px) {
    .jt-card { padding-block: var(--jt-space-4); padding-inline: var(--jt-space-4); }
  }
  .jt-card h4 { font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--jt-text-tertiary); margin: 0 0 12px; }

  /* ── Space card (grid) ── */
  .jt-space-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
  .jt-space-card { transition: all var(--jt-dur); cursor: pointer; }
  .jt-space-card:hover { border-color: var(--jt-border-strong); }

  /* ── Progress bar ── */
  .jt-progress { height: 4px; background: var(--jt-bg-muted); border-radius: 2px; overflow: hidden; }
  .jt-progress-fill { height: 100%; background: var(--jt-accent); border-radius: 2px; transition: width 0.8s var(--jt-ease); }

  /* ── Profile ── */
  .jt-profile { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); overflow: hidden; margin-bottom: 20px; }
  .jt-profile-banner { height: 120px; background: linear-gradient(135deg, var(--jt-accent), #1e40af); background: linear-gradient(135deg, var(--jt-accent), color-mix(in srgb, var(--jt-accent) 60%, black)); }
  .jt-profile-body { padding: 0 20px 20px; margin-top: -32px; }
  .jt-profile-av { width: 64px; height: 64px; border-radius: 50%; border: 3px solid var(--jt-bg); background: var(--jt-accent); color: var(--jt-accent-fg); display: flex; align-items: center; justify-content: center; font-size: 1.375em; font-weight: 700; margin-bottom: 10px; }
  .jt-profile-name { font-family: var(--jt-font-heading); font-size: 1.25rem; font-weight: 700; display: flex; align-items: center; gap: 8px; margin: 0; flex-wrap: wrap; row-gap: 8px; }
  .jt-profile-name > * { flex-shrink: 0; }
  .jt-level-tag { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); background: var(--jt-accent); color: var(--jt-accent-fg); white-space: nowrap; display: inline-block; }

  .jt-stats-bar { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 16px; padding: 12px; background: var(--jt-bg-subtle); border-radius: var(--jt-radius); }
  .jt-stat { text-align: center; }
  .jt-stat-n { font-family: var(--jt-font-mono); font-size: 1.15rem; font-weight: 700; }
  .jt-stat-l { font-size: 0.8125rem; color: var(--jt-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em; }

  /* ── Badges ── */
  .jt-badges { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .jt-badge-card { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 14px 8px; border-radius: var(--jt-radius); background: var(--jt-bg-subtle); transition: all var(--jt-dur); }
  .jt-badge-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
  .jt-badge-emoji { font-size: 1.5em; }
  .jt-badge-label { font-size: 0.9375rem; font-weight: 600; text-align: center; }
  .jt-badge-tier { font-size: 0.8125rem; font-family: var(--jt-font-mono); color: var(--jt-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em; }

  /* ── Kanban (ideas) ── */
  .jt-kanban { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
  .jt-col { background: var(--jt-bg-subtle); border-radius: var(--jt-radius-lg); padding: 12px; min-height: 350px; }
  .jt-col-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 2px solid; }
  .jt-col-title { font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
  .jt-col-n { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--jt-bg); }
  .jt-idea { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); padding: 12px; margin-bottom: 8px; cursor: pointer; transition: all var(--jt-dur); }
  .jt-idea:hover { border-color: var(--jt-border-strong); transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.04); }
  .jt-idea-title { font-weight: 600; line-height: 1.4; margin-bottom: 6px; }
  .jt-idea-votes { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; color: var(--jt-text-secondary); }
  .jt-idea-meta { display: flex; justify-content: space-between; margin-top: 6px; font-size: 0.9375rem; color: var(--jt-text-tertiary); }

  /* ── Leaderboard ── */
  .jt-leader { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid var(--jt-border); }
  .jt-leader:last-child { border-bottom: none; }
  /* "You are ranked #N" summary — accent-tinted card above the board. */
  .jt-leader-you { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; padding: 12px 16px; border-inline-start: 3px solid var(--jt-accent); background: var(--jt-accent-light, var(--jt-bg-subtle)); }
  .jt-leader-you-rank { font-family: var(--jt-font-mono); font-size: 1.25rem; font-weight: 700; color: var(--jt-accent); }
  .jt-leader-you-label { display: flex; flex-direction: column; line-height: 1.3; font-size: 0.9375rem; font-weight: 600; color: var(--jt-text); }
  .jt-leader-you-label small { font-size: 0.8125rem; font-weight: 400; color: var(--jt-text-tertiary); }
  /* Mobile-only column header — hidden on desktop. See ≤640px block. */
  .jt-leader-head { display: none; }
  .jt-leader-rank { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 700; color: var(--jt-text-tertiary); width: 20px; text-align: center; }
  /* Top-3 medal colors for the leaderboard award icon (Lucide award.svg
     uses currentColor for stroke; we tint via the wrapper). */
  .jt-medal { display: inline-flex; align-items: center; justify-content: center; line-height: 1; }
  .jt-medal-gold   { color: #d4a017; }
  .jt-medal-silver { color: #9aa0a6; }
  .jt-medal-bronze { color: #c08457; }
  .jt-leader-name { flex: 1; font-weight: 500; }
  .jt-leader-pts { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; color: var(--jt-text-secondary); }

  /* ── Trending ── */
  .jt-trend { display: flex; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--jt-border); }
  .jt-trend:last-child { border-bottom: none; }
  .jt-trend-n { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 700; color: var(--jt-border-strong); min-width: 24px; }
  .jt-trend-title { font-size: 0.9375rem; font-weight: 500; line-height: 1.4; cursor: pointer; }
  .jt-trend-title:hover { color: var(--jt-text-secondary); }
  .jt-trend-meta { font-size: 0.9375rem; color: var(--jt-text-tertiary); margin-top: 2px; }

  /* ── Tags cloud ── */
  .jt-tags { display: flex; flex-wrap: wrap; gap: 5px; }
  .jt-tags .jt-tag { cursor: pointer; transition: all 0.1s; }
  .jt-tags .jt-tag:hover { background: var(--jt-accent-muted); color: var(--jt-text-secondary); }

  /* ── Loading ── */
  .jt-loading { text-align: center; padding: 40px; color: var(--jt-text-tertiary); }
  .jt-spinner { width: 24px; height: 24px; border: 2px solid var(--jt-border); border-top-color: var(--jt-text-secondary); border-radius: 50%; animation: jt-spin 0.6s linear infinite; margin: 0 auto 8px; }
  @keyframes jt-spin { to { transform: rotate(360deg); } }

  /* ── Empty state ── */
  .jt-empty { background: var(--jt-bg-muted); text-align: center; padding: var(--jt-space-8) var(--jt-space-5); color: var(--jt-text-tertiary); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); }
  .jt-empty-icon { font-size: 3em; margin-bottom: 12px; }
  .jt-empty-text { font-size: 0.9375rem; margin-bottom: 16px; }

  /* ── Animations ── */
  @keyframes jt-fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
  .jt-anim { animation: jt-fadeIn 0.3s var(--jt-ease) both; }
  .jt-d1 { animation-delay: 0.04s; }
  .jt-d2 { animation-delay: 0.08s; }
  .jt-d3 { animation-delay: 0.12s; }
  .jt-d4 { animation-delay: 0.16s; }

  /* Mobile hamburger */
  .jt-mobile-toggle { display: none; width: 40px; height: 40px; border-radius: 50%; align-items: center; justify-content: center; color: var(--jt-text-secondary); border: none; background: none; cursor: pointer; }

  @media (max-width: 900px) {
    .jt-two-col { grid-template-columns: 1fr; }
    /* Stack the sidebar below content — never hide it. The About card,
       space stats, and the Members / Moderation / Edit-space links are
       unique content + navigation; the old `display:none` stranded every
       tablet and phone user without them. Drop sticky so it flows in the
       single-column order instead of pinning to the viewport. */
    .jt-two-col > .jt-sidebar,
    .jt-two-col > aside { position: static; top: auto; }
    .jt-kanban { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .jt-space-head { flex-direction: column; align-items: center; text-align: center; }
    .jt-space-nums { margin-left: 0; }
    .jt-header { padding: 10px 16px; }
    .jt-mobile-toggle { display: flex; }
    .jt-nav { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: var(--jt-bg); z-index: 200; flex-direction: column; align-items: center; justify-content: center; gap: 8px; }
    .jt-nav.open { display: flex; }
    .jt-nav.open a { font-size: 1.1rem; padding: 12px 24px; }
    .jt-search { display: none; }
    .jt-nav.open .jt-mobile-close { position: absolute; top: 16px; right: 16px; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; color: var(--jt-text-secondary); background: none; border: none; cursor: pointer; }
  }
  /* ═══════════════════════════════════════════════════════════════════
     RESPONSIVE OVERRIDES — see docs/DESIGN-SYSTEM.md for rules.
     Exactly two breakpoints, consolidated into two blocks below.
     Do NOT add new `@media` queries elsewhere in this file.
     ═══════════════════════════════════════════════════════════════════ */

  /* ── Tablet + below (≤1024px) ──────────────────────────────────────
     Nav row affordances: scroll fallback, hide font-scale. Typography
     and layout stay desktop-sized. Applies to phones too via cascade. */
  @media (max-width: 1024px) {
    .jt-community-nav-inner { gap: var(--jt-space-2); }
    .jt-community-nav-links { flex: 1 1 0; min-width: 0; overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
    .jt-community-nav-links::-webkit-scrollbar { display: none; }
    .jt-community-nav-links a { white-space: nowrap; flex: 0 0 auto; padding: var(--jt-space-3) var(--jt-space-3); font-size: var(--jt-text-base); min-height: var(--jt-tap); }
    .jt-community-nav-actions { flex: 0 0 auto; }
    .jt-community-nav-actions .jt-font-scale { display: none; }
  }

  /* ── Phone (≤640px) ────────────────────────────────────────────────
     Layout density drops one level. Typography scales down one step
     per the design system's mobile column. All interactive elements
     keep ≥40px tap targets via --jt-tap. */
  @media (max-width: 640px) {
    /* Container + app wrapper */
    .jt-container { padding: var(--jt-space-4); }
    /* Leave room for theme mobile bottom tab bar (Reign/BuddyNext/BuddyX). */
    .jt-app { padding-bottom: calc(var(--jt-space-12) + var(--jt-space-8)); }

    /* Topic listing rows — `.jt-row, a.jt-row` specificity match is
       load-bearing (desktop `a.jt-row` is 0,1,1). */
    .jt-row, a.jt-row { grid-template-columns: 36px 1fr 48px; gap: var(--jt-space-2); padding: var(--jt-space-3) var(--jt-space-3); }
    /* Drafts rows stack on mobile: the inline title|actions shape squeezes the
       title to a per-word column at 390px. One column = title full width, then
       the time + Publish-now actions row beneath it. */
    .jt-row.jt-row--draft { grid-template-columns: 1fr; }
    .jt-row.jt-row--draft .jt-row-stat { justify-content: flex-start; margin-top: var(--jt-space-2); }
    .jt-row .jt-votes { width: 36px; }
    .jt-row-title { font-size: var(--jt-text-base); line-height: var(--jt-leading-snug); }
    .jt-row-sub { font-size: var(--jt-text-xs); gap: var(--jt-space-2); margin-top: var(--jt-space-1); }
    .jt-row-sub .jt-tag { font-size: var(--jt-text-2xs); padding: 2px var(--jt-space-2); }
    .jt-row-sub .jt-tl { width: 16px; height: 16px; font-size: 0.625rem; }
    .jt-row-stat { text-align: right; }
    .jt-row-stat-n { font-size: var(--jt-text-sm); }
    .jt-row-stat-l { font-size: var(--jt-text-2xs); }
    .jt-row-stat:last-child { display: none; }

    /* Community nav: icon-only with title tooltip. Labels hide
       entirely; `title` + `aria-label` provide the accessible name. */
    .jt-community-nav-links a { padding: var(--jt-space-3) var(--jt-space-3); gap: 0; min-width: var(--jt-tap); justify-content: center; }
    .jt-community-nav-links a > .jt-nav-label { display: none; }

    /* Profile header — allow wrap, keep badges atomic */
    .jt-profile-name > .jt-level-tag { margin-left: var(--jt-space-2); }

    /* Multi-column grids collapse to single column on phones */
    .jt-kanban { grid-template-columns: 1fr; }
    .jt-badges { grid-template-columns: repeat(2, 1fr); }
    .jt-stats-bar { grid-template-columns: repeat(2, 1fr); }
    .jt-nested, .jt-nested-1, .jt-nested-2, .jt-nested-3 { margin-left: var(--jt-space-3); }

    /* Action bar — primary CTA goes full width */
    .jt-bar { flex-wrap: wrap; gap: var(--jt-space-2); }
    .jt-bar > .jt-btn-fill,
    .jt-bar > a.jt-btn-fill { order: 10; width: 100%; text-align: center; justify-content: center; }

    /* Buttons — compact on mobile without dropping below the 40px
       tap-target floor. Base `.jt-btn` has desktop padding 8px 18px
       and 15px font. On phones we drop to 8px 14px and 14px font so
       three buttons (View / Remove / Dismiss) fit a moderation card
       row without "Remove Content" wrapping to 2 lines. */
    .jt-app .jt-btn { padding: var(--jt-space-2) var(--jt-space-4); font-size: var(--jt-text-sm); min-height: var(--jt-tap); }
    .jt-app .jt-btn-sm { padding: 4px var(--jt-space-3); font-size: var(--jt-text-xs); min-height: var(--jt-tap); }

    /* Leaderboard rows — tighten stats, hide per-row rep/posts labels
       (they repeat 20× per page) and show a single sticky header row
       at the top of the card instead. Desktop keeps the per-row labels
       since space isn't an issue there. */
    .jt-leader { gap: var(--jt-space-2); padding: var(--jt-space-2) var(--jt-space-3); }
    .jt-leader-name { font-size: var(--jt-text-sm); min-width: 0; }
    .jt-leader-stats { gap: var(--jt-space-3); }
    .jt-leader-stat-val,
    .jt-leader-pts { font-size: var(--jt-text-sm); }
    .jt-leader-rank { font-size: var(--jt-text-sm); width: 18px; }

    /* Hide per-row "rep" and "posts" labels on mobile. The header row
       added above the list serves as the column legend. */
    .jt-leader:not(.jt-leader-head) .jt-leader-stat-lbl { display: none; }

    /* Show the mobile header row. It renders as a dimmed, uppercase
       column legend aligned with the stat values below. */
    .jt-leader-head { display: flex; align-items: center; background: var(--jt-bg-subtle); font-size: 0.625rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--jt-text-tertiary); font-weight: 600; padding: var(--jt-space-2) var(--jt-space-3); }
    .jt-leader-head .jt-leader-head-spacer { width: 36px; flex-shrink: 0; }
    .jt-leader-head .jt-leader-name { font-size: 0.625rem; font-weight: 600; color: var(--jt-text-tertiary); }
    .jt-leader-head .jt-leader-stats { color: var(--jt-text-tertiary); }
    .jt-leader-head .jt-leader-stats > div { min-width: 38px; text-align: right; }

    /* Notifications list + trending sidebar — apply scale */
    .jt-notif-text,
    .jt-trend-title,
    .jt-empty-text,
    .jt-col-title,
    .jt-badge-label { font-size: var(--jt-text-sm); }

    /* Page sub-titles */
    .jt-page-title-sm { font-size: var(--jt-text-xl); }

    /* Reply body + post body default to 1rem (16px) — keep body at
       15px on mobile for density without going under the iOS zoom
       threshold on input fields. */
    .jt-post-body { padding: var(--jt-space-4); font-size: var(--jt-text-base); }
    .jt-post-tags { padding-inline: var(--jt-space-4); }
    .jt-reply-body { padding: var(--jt-space-3) var(--jt-space-4); font-size: var(--jt-text-base); }

    /* Breadcrumb truncation */
    .jt-crumb { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; display: block; }
    .jt-crumb a, .jt-crumb span { display: inline; }

    /* Post head — smaller title, follow button flows inline below meta */
    .jt-post-head { padding: var(--jt-space-4); }
    .jt-post-head h1 { font-size: var(--jt-text-xl); padding-right: 0; }
    .jt-post-head > .jt-btn-sm { position: static; display: inline-flex; align-items: center; margin-top: var(--jt-space-4); min-height: var(--jt-tap); padding: var(--jt-space-2) var(--jt-space-5); }

    /* Post / reply foot — icon-only single row on mobile. Padding
       tightened from 10px 16px to 8px so all 7–8 actions fit on a
       390px viewport. Item spacing stays compact, but the tap target is
       a full 44px (touch standard) — the row already wraps, so taller
       buttons wrap to a second line rather than overflowing. Text labels
       hide; `title` + `aria-label` carry the accessible name. */
    .jt-post-foot,
    .jt-reply-foot { flex-wrap: wrap; gap: 2px; padding: var(--jt-space-2); align-items: center; justify-content: flex-start; }
    .jt-post-foot .jt-act,
    .jt-reply-foot .jt-act { padding: 6px 8px; min-height: 44px; gap: 3px; font-size: var(--jt-text-xs); }
    .jt-post-foot .jt-more-trigger,
    .jt-reply-foot .jt-more-trigger { min-width: 44px; min-height: 44px; padding: 6px; }
    .jt-post-foot .jt-view-count,
    .jt-reply-foot .jt-view-count { padding: 0 4px; font-size: var(--jt-text-xs); }
    .jt-post-foot .jt-reactions,
    .jt-reply-foot .jt-reactions { margin: 0; }
    .jt-post-foot .jt-reaction-trigger,
    .jt-reply-foot .jt-reaction-trigger { min-width: 34px; min-height: 34px; padding: 6px; }
    .jt-post-foot .jt-btn-label,
    .jt-reply-foot .jt-btn-label { display: none; }

    /* Reply composer footer stacks vertically */
    .jt-editor-foot { flex-direction: column; gap: var(--jt-space-2); align-items: stretch; }
    .jt-editor-foot .jt-btn { width: 100%; justify-content: center; }
  }

  .jt-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
  .jt-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .jt-hidden { display: none !important; }
  .jt-flex { display: flex; }
  .jt-flex-col { display: flex; flex-direction: column; }
  .jt-items-center { align-items: center; }
  .jt-items-start { align-items: flex-start; }
  .jt-justify-between { justify-content: space-between; }
  .jt-flex-1 { flex: 1; min-width: 0; }
  .jt-flex-shrink-0 { flex-shrink: 0; }
  .jt-ml-auto { margin-left: auto; }
  .jt-gap-xs { gap: 4px; }
  .jt-gap-sm { gap: 8px; }
  .jt-gap-10 { gap: 10px; }
  .jt-gap-md { gap: 16px; }
  .jt-gap-12 { gap: 12px; }
  .jt-mt-sm { margin-top: 8px; }
  .jt-mt-10 { margin-top: 10px; }
  .jt-mt-md { margin-top: 16px; }
  .jt-mt-lg { margin-top: 24px; }
  .jt-mb-sm { margin-bottom: 8px; }
  .jt-mb-10 { margin-bottom: 10px; }
  .jt-mb-12 { margin-bottom: 12px; }
  .jt-mb-md { margin-bottom: 16px; }
  .jt-mb-20 { margin-bottom: 20px; }
  .jt-mb-lg { margin-bottom: 24px; }
  .jt-p-md { padding: 16px; }
  .jt-w-full { width: 100%; }
  .jt-text-center { text-align: center; }
  .jt-text-right { text-align: right; }
  .jt-block { display: block; }
  .jt-no-underline { text-decoration: none; }

  /* ── Category header ── */
  .jt-cat-row { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
  .jt-cat-emoji { font-size: 1.25em; }
  .jt-cat-name { font-family: var(--jt-font-heading); font-size: 1.25rem; font-weight: 700; margin: 0; }
  .jt-cat-desc { font-size: 0.9375rem; color: var(--jt-text-tertiary); }
  .jt-cat-empty { color: var(--jt-text-tertiary); padding: 8px 0; }

  /* ── Space card (home/category) ── */
  .jt-space-card-inner { display: flex; align-items: center; gap: 12px; }
  .jt-space-card-emoji { font-size: 1.75em; flex-shrink: 0; line-height: 1; }
  .jt-space-card-body { min-width: 0; flex: 1; }
  .jt-space-card-title { font-size: 0.9375rem; font-weight: 600; color: var(--jt-text); margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .jt-space-card-excerpt { font-size: 0.8125rem; color: var(--jt-text-tertiary); line-height: 1.4; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .jt-space-card-stats { display: flex; gap: 12px; margin-top: 6px; }
  .jt-space-card-stat { font-size: 0.8125rem; color: var(--jt-text-tertiary); }
  .jt-space-card-stat strong { color: var(--jt-text-secondary); font-weight: 600; }
  /* Recency signal on space cards (1.5.0) — pushed to the end, lighter, so a
     newcomer can tell at a glance which spaces are alive. */
  .jt-space-card-activity { margin-inline-start: auto; font-style: italic; }
  .jt-space-card-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-block-end: 6px; }
  .jt-space-card-badge { display: inline-flex; align-items: center; gap: 4px; padding-block: 2px; padding-inline: 6px; font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.02em; border-radius: var(--jt-radius-sm, 6px); background: var(--jt-bg-muted); color: var(--jt-text-secondary); line-height: 1; }
  .jt-space-card-badge svg { flex-shrink: 0; width: 12px; height: 12px; }
  .jt-space-card-badge-hidden { background: var(--jt-warn-light, var(--jt-bg-muted)); color: var(--jt-warn, var(--jt-text-secondary)); }
  /*
   * Type chips. Tinted with the accent token so the chip reads as
   * "category" rather than "warning", and stays automatically theme-
   * adaptive — `--jt-accent-light` flips for dark mode via the same
   * cascade that powers the rest of the UI.
   */
  .jt-space-card-badge-type { background: var(--jt-accent-light, var(--jt-bg-muted)); color: var(--jt-accent, var(--jt-text-secondary)); }
  /*
   * Policy chips. Approval-required reads as a soft warning (action
   * still owed by the visitor); invite-only reads as informational
   * (no action owed, just a contract statement).
   */
  .jt-space-card-badge-policy-approval { background: var(--jt-warn-light, var(--jt-bg-muted)); color: var(--jt-warn, var(--jt-text-secondary)); }
  .jt-space-card-badge-policy-invite { background: var(--jt-bg-muted); color: var(--jt-text-secondary); }

  /* ── Space status banner ── */
  .jt-status-banner { padding: 10px 16px; border-radius: var(--jt-radius); font-size: 0.9375rem; font-weight: 500; margin-bottom: 12px; }
  .jt-status-banner--archived { background: var(--jt-bg-muted); color: var(--jt-text-secondary); border: 1px solid var(--jt-border); }
  .jt-status-banner--locked { background: var(--jt-warn-light); color: var(--jt-warn-dark); border: 1px solid var(--jt-warn-border); }

  /* ── Status badges ── */
  .jt-badge-resolved { background: var(--jt-success-light); color: var(--jt-success); font-size: 0.8125rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); }
  .jt-badge-closed { background: var(--jt-bg-muted); color: var(--jt-text-tertiary); font-size: 0.8125rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); }
  .jt-badge-danger { display: inline-flex; align-items: center; background: var(--jt-danger); color: var(--jt-white); font-size: 0.8125rem; font-weight: 700; padding: 3px 10px; border-radius: var(--jt-radius-full); white-space: nowrap; }
  .jt-badge-private { background: var(--jt-warn-light); color: var(--jt-warn); font-size: 0.8125rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); display: inline-flex; align-items: center; gap: 3px; }
  /* Pinned (sticky) topic marker — same shape as the private badge but in the
     accent colour, since pinning is informational, not a warning. Labelled
     (not an aria-hidden bare icon) so it reads as "Pinned" for everyone. */
  .jt-badge-pinned { background: var(--jt-accent-muted); color: var(--jt-accent); font-size: 0.8125rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); display: inline-flex; align-items: center; gap: 3px; }
  .jt-badge-pinned svg { flex-shrink: 0; }
  .jt-prefix { --jt-pfx: var(--jt-accent); background: color-mix(in srgb, var(--jt-pfx) 15%, transparent); color: var(--jt-pfx); font-size: 0.75rem; font-weight: 700; padding: 2px 8px; border-radius: var(--jt-radius-sm); text-transform: uppercase; letter-spacing: 0.03em; white-space: nowrap; }
  .jt-badge-accent { font-size: 0.8125rem; font-weight: 600; background: var(--jt-accent-muted); color: var(--jt-text-secondary); padding: 2px 8px; border-radius: var(--jt-radius-full); }

  /* Feed-space card layout — full body inline like a social feed.
     Distinct from the dense topic-list .jt-row used by Forum/QA/Ideas
     so the visual hierarchy matches the type's contract. */
  .jt-feed { display: flex; flex-direction: column; gap: 12px; }
  .jt-feed-card { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); padding: 16px; }
  .jt-feed-card-head { display: flex; align-items: center; gap: var(--jt-space-3); flex-wrap: wrap; margin-block-end: var(--jt-space-3); }
  .jt-feed-card-meta { display: inline-flex; align-items: center; gap: var(--jt-space-2); flex-wrap: wrap; min-width: 0; line-height: 1.4; }
  .jt-feed-card-author { font-weight: 600; font-size: 0.9375rem; color: var(--jt-text); text-decoration: none; }
  .jt-feed-card-author:hover { text-decoration: underline; color: var(--jt-text); }
  .jt-feed-card-time { color: var(--jt-text-tertiary); font-size: 0.9375rem; text-decoration: none; }
  .jt-feed-card-time:hover { color: var(--jt-text-secondary); text-decoration: underline; }
  .jt-feed-card-body { color: var(--jt-text); line-height: 1.6; word-break: break-word; }
  .jt-feed-card-body p { margin-block: 0 12px; }
  .jt-feed-card-body p:last-child { margin-block-end: 0; }
  .jt-feed-card-body img { max-width: 100%; height: auto; border-radius: var(--jt-radius-sm); }
  .jt-feed-card-foot { display: flex; align-items: center; gap: 4px; margin-block-start: 14px; padding-block-start: 12px; border-block-start: 1px solid var(--jt-border); }
  .jt-feed-act { display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 10px; background: transparent; border: 0; color: var(--jt-text-secondary); font: inherit; font-size: 0.875rem; cursor: pointer; border-radius: var(--jt-radius-sm); text-decoration: none; transition: background var(--jt-dur) var(--jt-ease), color var(--jt-dur) var(--jt-ease); }
  .jt-feed-act:hover { background: var(--jt-bg-hover); color: var(--jt-text); }
  .jt-feed-act:focus-visible { background: var(--jt-bg-hover); color: var(--jt-text); outline: 2px solid var(--jt-accent); outline-offset: 2px; }
  .jt-feed-act.jt-voted, .jt-feed-act.voted { color: var(--jt-accent); }
  .jt-feed-act svg { flex-shrink: 0; }
  .jt-feed-act-n { font-weight: 600; font-variant-numeric: tabular-nums; }
  .jt-feed-act-permalink { margin-inline-start: auto; color: var(--jt-text-tertiary); }
  @media (max-width: 640px) {
    .jt-feed-card { padding: 12px; }
    .jt-feed-card-foot { flex-wrap: wrap; }
  }

  /* Q&A "Answered" / "Needs answer" pill on post cards. Same visual
     language as the idea-status pill so listings stay scannable across
     space types. Answered = success green; Needs answer = quiet neutral. */
  .jt-qa-pill { display: inline-flex; align-items: center; gap: 4px; font-size: 0.75rem; font-weight: 700; padding: 2px 8px; border-radius: var(--jt-radius-full); letter-spacing: 0.02em; white-space: nowrap; }
  .jt-qa-pill-answered { background: color-mix(in srgb, var(--jt-success) 16%, transparent); color: var(--jt-success); }
  .jt-qa-pill-needs-answer { background: var(--jt-bg-muted); color: var(--jt-text-secondary); }

  /* Pinned accepted-answer callout above the reply list on Q&A posts.
     Visually echoes the reply's "Accepted" badge color so customers
     immediately recognise this is the answer, while the reply itself
     still appears in chronological position below. */
  .jt-accepted-callout { background: color-mix(in srgb, var(--jt-success) 8%, transparent); border: 1px solid color-mix(in srgb, var(--jt-success) 30%, transparent); border-radius: var(--jt-radius); padding: 12px 14px; margin-block: 8px 16px; }
  .jt-accepted-callout-head { display: inline-flex; align-items: center; gap: 6px; color: var(--jt-success); font-weight: 700; font-size: 0.8125rem; letter-spacing: 0.02em; text-transform: uppercase; margin-block-end: 8px; }
  .jt-accepted-callout-body .jt-reply { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-sm); padding: 12px; }

  /* Idea roadmap status pill - four canonical statuses, color-coded so the
     listing and post header speak the same visual language as the kanban. */
  .jt-idea-pill { --jt-ip: var(--jt-text-secondary); display: inline-flex; align-items: center; background: color-mix(in srgb, var(--jt-ip) 14%, transparent); color: var(--jt-ip); font-size: 0.75rem; font-weight: 700; padding: 2px 8px; border-radius: var(--jt-radius-full); letter-spacing: 0.02em; white-space: nowrap; }
  .jt-idea-pill-planned { --jt-ip: var(--jt-accent); }
  .jt-idea-pill-in_progress { --jt-ip: var(--jt-warn); }
  .jt-idea-pill-shipped { --jt-ip: var(--jt-success); }
  .jt-idea-pill-declined { --jt-ip: var(--jt-text-tertiary); }

  /* Roadmap status picker (moderator only on Ideas spaces). Each option
     is a pill button that mirrors the read-only pill above; the active
     option uses a heavier border + filled background so the choice is
     obvious without dominating the post header. */
  .jt-idea-status-setter { display: flex; align-items: center; gap: var(--jt-space-3); flex-wrap: wrap; margin: 0; padding: var(--jt-space-3) var(--jt-space-4); background: var(--jt-bg-subtle); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); }
  .jt-idea-status-setter-label { font-size: 0.8125rem; font-weight: 600; color: var(--jt-text-secondary); }
  .jt-idea-status-options { display: flex; flex-wrap: wrap; gap: 6px; }
  /* Inactive setter buttons render neutral so the ONE active status is
     visually obvious. Only the active button wears its semantic color
     (planned=blue, in_progress=warn, shipped=success, declined=tertiary).
     Hover previews the semantic color as a tint so the affordance is
     still legible. */
  .jt-idea-status-btn { font-family: inherit; cursor: pointer; background: var(--jt-bg); color: var(--jt-text-secondary); border: 1px solid var(--jt-border); line-height: 1.3; transition: background var(--jt-dur) var(--jt-ease), border-color var(--jt-dur) var(--jt-ease), transform var(--jt-dur) var(--jt-ease), color var(--jt-dur) var(--jt-ease); }
  .jt-idea-status-btn:hover, .jt-idea-status-btn:focus-visible { background: color-mix(in srgb, var(--jt-ip) 12%, transparent); color: var(--jt-ip); border-color: color-mix(in srgb, var(--jt-ip) 30%, transparent); outline: none; }
  .jt-idea-status-btn:focus-visible { box-shadow: 0 0 0 2px var(--jt-bg), 0 0 0 4px var(--jt-ip); }
  .jt-idea-status-btn.is-active { background: var(--jt-ip); color: #fff; border-color: var(--jt-ip); }
  .jt-idea-status-btn[disabled] { opacity: 0.6; cursor: progress; }
  @media (max-width: 640px) {
    .jt-idea-status-setter { gap: var(--jt-space-2); padding: var(--jt-space-3); align-items: flex-start; flex-direction: column; }
    .jt-idea-status-options { width: 100%; }
    .jt-idea-status-btn { flex: 0 0 auto; }
  }

  /* Posts | Roadmap tabs on Ideas spaces. Plain underline-on-active tab
     row, mobile-first single line that wraps if the labels grow. */
  .jt-space-tabs { display: flex; gap: 4px; border-block-end: 1px solid var(--jt-border); margin-block: 12px 16px; }
  .jt-space-tab { display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px; font-size: 0.9375rem; font-weight: 600; color: var(--jt-text-secondary); text-decoration: none; border-block-end: 2px solid transparent; transition: color var(--jt-dur) var(--jt-ease), border-color var(--jt-dur) var(--jt-ease); }
  .jt-space-tab:hover, .jt-space-tab:focus-visible { color: var(--jt-text); }
  .jt-space-tab.on { color: var(--jt-accent); border-block-end-color: var(--jt-accent); }
  @media (max-width: 640px) { .jt-space-tabs { gap: 0; overflow-x: auto; -webkit-overflow-scrolling: touch; } .jt-space-tab { white-space: nowrap; } }

  /* ── Private toggle on new post form ── */
  /* Token-aligned gap above the Private toggle so it doesn't sit flush against
     the preceding composer row (e.g. the Add Poll button when Pro is active). */
  .jt-private-toggle { display: flex; align-items: center; margin-block-start: var(--jt-space-4, 16px); }
  .jt-checkbox-label { display: flex; align-items: center; gap: 6px; font-size: 0.875rem; color: var(--jt-text-secondary); cursor: pointer; }
  .jt-checkbox-label input[type="checkbox"] { width: 1rem; height: 1rem; accent-color: var(--jt-accent); }

  /* ── Similar Topics panel ── */
  .jt-similar { margin-top: 8px; border: 1px solid var(--jt-border); border-radius: var(--jt-radius); overflow: hidden; }
  .jt-similar[hidden] { display: none; }
  .jt-similar-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: var(--jt-bg-subtle); border-bottom: 1px solid var(--jt-border); }
  .jt-similar-label { font-size: 0.8125rem; font-weight: 600; color: var(--jt-text-secondary); }
  .jt-similar-toggle { font-size: 0.8125rem; color: var(--jt-text-tertiary); cursor: pointer; display: flex; align-items: center; gap: 4px; }
  .jt-similar-toggle input { width: 0.875rem; height: 0.875rem; accent-color: var(--jt-accent); }
  .jt-similar-item { display: block; padding: 8px 12px; text-decoration: none; border-bottom: 1px solid var(--jt-border); transition: background 0.1s; }
  .jt-similar-item:last-child { border-bottom: none; }
  .jt-similar-item:hover { background: var(--jt-bg-hover); }
  .jt-similar-title { display: block; font-size: 0.875rem; font-weight: 500; color: var(--jt-text); }
  .jt-similar-meta { display: block; font-size: 0.75rem; color: var(--jt-text-tertiary); margin-top: 2px; }

  /* ── Quote blockquote ── */
  .jt-quote { border-left: 3px solid var(--jt-accent); background: var(--jt-bg-subtle); padding: 10px 14px; border-radius: 0 var(--jt-radius-sm) var(--jt-radius-sm) 0; margin: 0 0 12px; font-size: 0.9375rem; }
  .jt-quote cite { display: block; font-size: 0.8125rem; font-weight: 600; color: var(--jt-accent); font-style: normal; margin-bottom: 4px; }
  .jt-quote cite::before { content: '\2014 '; }
  .jt-quote p { margin: 0; color: var(--jt-text-secondary); line-height: 1.5; }

  /* ── Inline notices (pending, spam, etc.) ── */
  .jt-notice { padding: 10px 16px; border-radius: var(--jt-radius); font-size: 0.9375rem; margin-bottom: 16px; }
  .jt-notice-warning { background: var(--jt-warn-light); color: var(--jt-warn-dark); border-left: 4px solid var(--jt-warn); }

  /* ── View count ── */
  .jt-view-count { display: inline-flex; align-items: center; gap: 4px; font-size: var(--jt-text-sm); color: var(--jt-text-tertiary); }
  .jt-view-count .n { font-family: var(--jt-font-mono); font-weight: 700; }

  /* ── Mod/edit actions ── */
  .jt-actions-group { display: flex; gap: 4px; margin-left: 8px; }

  /* ── Empty with padding ── */
  .jt-empty-compact { text-align: center; padding: 30px 20px; color: var(--jt-text-tertiary); }

  /* ── Empty-state partial: tones ── */
  .jt-empty-tone-warn .jt-empty-text { color: var(--jt-text-secondary); }
  .jt-empty-tone-forbidden .jt-empty-icon { color: var(--jt-text-tertiary); }
  .jt-empty-tone-forbidden .jt-empty-text { color: var(--jt-text-secondary); font-weight: 600; }
  .jt-empty-desc { color: var(--jt-text-tertiary); font-size: var(--jt-text-sm); margin: 8px 0 0; }
  .jt-empty-cta { margin-top: 16px; }

  /* ── Space gate (private/invite/approval) standalone form / actions below empty-state ── */
  .jt-space-gate-form { max-inline-size: 480px; margin-inline: auto; margin-block-start: 16px; display: flex; flex-direction: column; gap: 12px; }
  .jt-space-gate-actions { text-align: center; margin-block-start: 16px; }

  /* ── Reply composer wrapper ── */
  .jt-reply-composer { margin-top: var(--jt-space-6); }
  .jt-reply-composer h3, .jt-reply-composer h4 { font-weight: 600; margin: 0 0 var(--jt-space-4); }

  /* ── Closed post notice ── */
  .jt-closed-notice { text-align: center; padding: 20px; color: var(--jt-text-tertiary); background: var(--jt-bg-subtle); border-radius: var(--jt-radius); margin-top: 20px; }

  /* ── Leaderboard ── */
  /* Leaderboard uses jt-two-col now — no max-width needed */
  .jt-page-title { font-family: var(--jt-font-heading); font-size: 1.375rem; font-weight: 700; margin: 0; }
  /* Community-home title: now visible (was screen-reader-only, which contradicted
     the "main heading" setting). Spacing so it reads as the page heading.
     .jt-app prefix: a bare class (0,1,0) loses its margin to the
     .jt-app h1 reset (0,1,1). */
  .jt-app .jt-home-title { margin: 0 0 var(--jt-space-5, 20px); }
  /* Newcomer welcome hero (1.5.0) — anon-only orientation block. Tokens only,
     so it inverts cleanly in dark mode. */
  .jt-home-welcome {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.25rem;
    border: 1px solid var(--jt-border);
    border-radius: var(--jt-radius-lg, 12px);
    background: var(--jt-bg-subtle, var(--jt-bg));
  }
  .jt-home-welcome-body { flex: 1 1 320px; min-width: 0; }
  .jt-app .jt-home-welcome-title { margin: 0 0 0.35rem; font-size: 1.25rem; }
  .jt-home-welcome-sub { margin: 0 0 0.75rem; color: var(--jt-text-secondary); max-width: 60ch; }
  .jt-home-welcome-pulse { display: flex; flex-wrap: wrap; gap: 1rem; font-size: 0.875rem; color: var(--jt-text-tertiary); }
  .jt-home-welcome-pulse .jt-pulse-stat strong { color: var(--jt-text); font-weight: 700; }
  .jt-pulse-stat--live strong { color: var(--jt-accent); }
  .jt-home-welcome-actions { display: flex; gap: 0.5rem; flex-shrink: 0; }
  .jt-page-title-sm { font-size: 1.25rem; }
  .jt-leader-stats { display: flex; gap: 16px; margin-left: auto; text-align: right; }
  .jt-leader-stat-val { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; }
  .jt-leader-stat-lbl { font-size: 0.8125rem; color: var(--jt-text-tertiary); text-transform: uppercase; letter-spacing: .05em; }
  .jt-leader-pad { padding: 10px 0; }

  /* ── Sidebar extras ── */
  .jt-sidebar-link { margin-top: 10px; }
  .jt-sidebar-link a { font-size: 0.9375rem; color: var(--jt-text-secondary); }
  .jt-sidebar-about { color: var(--jt-text-secondary); line-height: 1.5; margin-bottom: 12px; }
  .jt-sidebar-stats { display: flex; gap: 20px; margin-bottom: 12px; }
  .jt-sidebar-stat { display: flex; flex-direction: column; }
  .jt-sidebar-stat strong { font-family: var(--jt-font-mono); font-size: 1.125rem; font-weight: 700; }
  .jt-sidebar-stat span { font-size: 0.8125rem; color: var(--jt-text-tertiary); text-transform: uppercase; letter-spacing: 0.05em; }
  .jt-sidebar-meta { display: flex; gap: 6px; flex-wrap: wrap; }
  .jt-sidebar-link-text { font-size: 0.9375rem; color: var(--jt-accent); font-weight: 500; }
  .jt-tag-count { color: var(--jt-text-tertiary); font-size: 0.9375rem; margin-left: 3px; }

  /* ── Managed by sidebar card (1.4.0 G1) ───────────────────────────── */
  .jt-managed-by-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .jt-managed-by-row {
    display: flex;
    align-items: center;
    gap: 10px;
    justify-content: space-between;
  }
  .jt-managed-by-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--jt-text);
    text-decoration: none;
    min-width: 0;
    flex: 1 1 auto;
  }
  .jt-managed-by-link:hover .jt-managed-by-name,
  .jt-managed-by-link:focus-visible .jt-managed-by-name {
    color: var(--jt-accent);
  }
  .jt-managed-by-avatar {
    width: 32px;
    height: 32px;
    border-radius: var(--jt-radius-full);
    object-fit: cover;
    flex: 0 0 auto;
  }
  .jt-managed-by-name {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--jt-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color 0.15s ease;
  }
  .jt-managed-by-role {
    flex: 0 0 auto;
    padding: 2px 8px;
    border-radius: var(--jt-radius-full);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }
  .jt-managed-by-role--admin {
    background: var(--jt-accent-muted);
    color: var(--jt-accent);
  }
  .jt-managed-by-role--moderator {
    background: var(--jt-warn-light);
    color: var(--jt-warn);
  }
  .jt-managed-by-empty {
    margin: 0;
    color: var(--jt-text-tertiary);
    font-size: 0.875rem;
    line-height: 1.5;
  }

  /* ── Role pills on post / reply cards (1.4.0 G3) ──────────────────── */
  .jt-role-pill {
    display: inline-block;
    padding: 1px 6px;
    border-radius: var(--jt-radius-full);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    line-height: 1.4;
    vertical-align: middle;
  }
  .jt-role-pill--admin {
    background: var(--jt-accent-muted);
    color: var(--jt-accent);
  }
  .jt-role-pill--moderator {
    background: var(--jt-warn-light);
    color: var(--jt-warn);
  }

  /* ── Notification page ── */
  .jt-notifications-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
  }

  .jt-notifications-head__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }

  .jt-notif-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 16px;
    border-bottom: 1px solid var(--jt-border);
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }

  .jt-notif-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    text-decoration: none;
    color: var(--jt-text-secondary);
    border-bottom: 2px solid transparent;
    font-size: 0.875rem;
    font-weight: 500;
    white-space: nowrap;
    transition: color 0.15s, border-color 0.15s;
  }

  .jt-notif-tab:hover { color: var(--jt-text); }

  .jt-notif-tab.is-active {
    color: var(--jt-accent);
    border-bottom-color: var(--jt-accent);
  }

  .jt-notif-tab__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 6px;
    border-radius: 9px;
    background: var(--jt-bg-muted);
    color: var(--jt-text-secondary);
    font-size: 0.75rem;
    font-weight: 600;
  }

  .jt-notif-tab.is-active .jt-notif-tab__count {
    background: var(--jt-accent);
    color: var(--jt-bg);
  }

  .jt-notif-bulkbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 10px 16px;
    margin-bottom: 12px;
    background: var(--jt-bg-subtle);
    border: 1px solid var(--jt-border);
    border-radius: var(--jt-radius);
  }

  .jt-notif-bulkbar[hidden] { display: none; }

  .jt-notif-bulkbar__selectall {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.875rem;
    color: var(--jt-text);
    cursor: pointer;
  }

  .jt-notif-bulkbar__count {
    font-size: 0.8125rem;
    color: var(--jt-text-secondary);
  }

  .jt-notif-bulkbar__actions {
    display: flex;
    gap: 6px;
    margin-inline-start: auto;
  }

  /* ── Notification list ── */
  /* Notifications uses jt-two-col now — no max-width needed */
  .jt-notif-list { overflow: hidden; }

  .jt-notif-item {
    display: flex;
    align-items: stretch;
    border-bottom: 1px solid var(--jt-border);
    color: inherit;
    position: relative;
  }

  .jt-notif-item:last-child { border-bottom: none; }

  .jt-notif-item.unread { background: var(--jt-accent-muted); }

  .jt-notif-item__select {
    display: flex;
    align-items: flex-start;
    padding: 16px 4px 16px 14px;
    cursor: pointer;
  }

  .jt-notif-item__select input[type="checkbox"] {
    margin: 2px 0 0;
    cursor: pointer;
  }

  .jt-notif-item__link {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    flex: 1;
    min-width: 0;
    padding: 14px 12px;
    text-decoration: none;
    color: inherit;
  }

  .jt-notif-body { flex: 1; min-width: 0; }
  .jt-notif-text { font-size: 0.9375rem; line-height: 1.5; }
  .jt-notif-time { font-size: 0.75rem; color: var(--jt-text-tertiary); margin-top: 2px; }
  .jt-notif-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--jt-accent); flex-shrink: 0; margin-top: 6px; }

  .jt-notif-item__menu {
    position: relative;
    display: flex;
    align-items: flex-start;
    padding: 12px 8px 12px 4px;
  }

  .jt-notif-item__menu-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--jt-text-tertiary);
    border-radius: var(--jt-radius-sm);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }

  .jt-notif-item__menu-trigger:hover,
  .jt-notif-item__menu-trigger[aria-expanded="true"] {
    background: var(--jt-bg-hover);
    color: var(--jt-text);
  }

  .jt-notif-item__menu-trigger:focus-visible {
    outline: 2px solid var(--jt-accent);
    outline-offset: 2px;
  }

  .jt-notif-item__menu-list {
    /* Position is owned by SmartDropdown: it sets position:fixed + top/left
       inline on open. Do NOT set top/left/inset-inline-end here — a stale
       inset-inline-end combines with the JS-set left to stretch the fixed
       panel full-width across the viewport. min-width gives it a sane size. */
    position: absolute;
    z-index: 30;
    min-width: 160px;
    margin: 0;
    padding: 4px;
    list-style: none;
    background: var(--jt-bg);
    border: 1px solid var(--jt-border);
    border-radius: var(--jt-radius);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  }

  .jt-notif-item__menu-list[hidden] { display: none; }

  .jt-notif-item__menu-list li { margin: 0; }

  .jt-notif-item__menu-list button {
    display: block;
    width: 100%;
    padding: 8px 12px;
    text-align: start;
    border: 0;
    background: transparent;
    color: var(--jt-text);
    font-size: 0.875rem;
    border-radius: var(--jt-radius-sm);
    cursor: pointer;
  }

  .jt-notif-item__menu-list button:hover { background: var(--jt-bg-hover); }

  .jt-notif-item__menu-list .jt-notif-menu-danger { color: var(--jt-danger); }
  .jt-notif-item__menu-list .jt-notif-menu-danger:hover { background: var(--jt-danger-light); }

  @media (max-width: 600px) {
    .jt-notif-item__select { padding-inline-start: 10px; padding-inline-end: 2px; }
    .jt-notif-item__link { padding: 12px 8px; gap: 10px; }
    .jt-notif-item__menu { padding-inline-start: 0; padding-inline-end: 6px; }
  }

  /* ── Moderation ── */
  /* Moderation — full width in container */
  /* Moderation queue flag cards — token-driven per DESIGN-SYSTEM.md */
  .jt-mod-flag { padding: var(--jt-space-4); border-bottom: 1px solid var(--jt-border); }
  .jt-mod-flag-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--jt-space-2); margin-bottom: var(--jt-space-2); }
  .jt-mod-flag-type { font-size: var(--jt-text-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; padding: 2px var(--jt-space-2); border-radius: var(--jt-radius-full); background: var(--jt-danger-light); color: var(--jt-danger); white-space: nowrap; }
  .jt-mod-flag-reason { font-size: var(--jt-text-sm); font-weight: 500; color: var(--jt-text-secondary); }
  .jt-mod-flag-reporter { margin-left: auto; font-size: var(--jt-text-xs); color: var(--jt-text-tertiary); white-space: nowrap; }
  .jt-mod-flag-note { font-size: var(--jt-text-sm); color: var(--jt-text-secondary); margin-bottom: var(--jt-space-2); padding: var(--jt-space-2); background: var(--jt-bg-subtle); border-radius: var(--jt-radius-sm); }
  .jt-mod-flag-actions { display: flex; flex-wrap: wrap; gap: var(--jt-space-2); align-items: center; }
  /* Compact buttons inside moderation rows — the base .jt-btn is hero-sized
     (40px tall, 16.5px bold) which overpowers the flag reason/note text. */
  .jt-app .jt-mod-flag-actions .jt-btn {
    padding: 0.3rem 0.75rem;
    font-size: var(--jt-text-sm, 0.85rem);
    font-weight: 500;
    line-height: 1.25;
    border-radius: var(--jt-radius-sm, 6px);
    min-height: 0;
    gap: 4px;
  }
  .jt-app .jt-mod-flag-actions .jt-btn > .dashicons,
  .jt-app .jt-mod-flag-actions .jt-btn > svg { width: 14px; height: 14px; font-size: 14px; }
  @media (max-width: 640px) {
    .jt-app .jt-mod-flag-actions .jt-btn {
      padding: 0.45rem 0.75rem;
      font-size: 0.88rem;
    }
  }
  .jt-app .jt-btn-danger, .jt-app a.jt-btn-danger { background: var(--jt-danger); color: var(--jt-white); }
  .jt-app .jt-btn-danger:hover { color: var(--jt-white); }

  /* Resolve error surfaced by assets/js/moderation.js when REST fails. */
  .jt-mod-flag-error { color: var(--jt-danger); font-size: var(--jt-text-sm); margin-top: var(--jt-space-2); }

  /* Cross-space moderation dashboard at /community/mod/.
     1.4.3 redesign: the queue is rendered as a list of actual pending
     flags, not a navigation grid of space cards. A moderator sees the
     content excerpt, reason, reporter, and age inline — they only click
     through to /s/:slug/mod/ when they're ready to act. */
  .jt-mod-dashboard-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--jt-space-4); margin-block-end: var(--jt-space-5); }
  .jt-mod-flag-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--jt-space-3); }
  .jt-mod-flag-row { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); padding: var(--jt-space-4); display: flex; flex-direction: column; gap: var(--jt-space-3); transition: border-color .15s ease; }
  .jt-mod-flag-row:hover { border-color: var(--jt-border-strong); }
  .jt-mod-flag-row-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--jt-space-2); }
  .jt-mod-flag-row .jt-mod-flag-reason { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; padding: 2px var(--jt-space-2); border-radius: var(--jt-radius-full); background: var(--jt-bg-muted); color: var(--jt-text-secondary); }
  .jt-mod-flag-reason--spam { background: var(--jt-danger-light); color: var(--jt-danger); }
  .jt-mod-flag-reason--offensive,
  .jt-mod-flag-reason--harassment { background: var(--jt-warn-light); color: var(--jt-warn); }
  .jt-mod-flag-reason--off_topic { background: var(--jt-accent-light); color: var(--jt-accent); }
  .jt-mod-flag-row .jt-mod-flag-type { font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--jt-text-tertiary); background: transparent; padding: 0; }
  .jt-mod-flag-space { font-size: 0.875rem; font-weight: 500; color: var(--jt-text); text-decoration: none; }
  .jt-mod-flag-space:hover { color: var(--jt-accent); text-decoration: underline; }
  .jt-mod-flag-age { margin-inline-start: auto; font-size: 0.8125rem; color: var(--jt-text-tertiary); }
  .jt-mod-flag-title { font-weight: 600; font-size: 0.9375rem; color: var(--jt-text); }
  .jt-mod-flag-excerpt { font-size: 0.9375rem; color: var(--jt-text-secondary); line-height: 1.5; word-break: break-word; }
  .jt-mod-flag-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--jt-space-3); padding-block-start: var(--jt-space-2); border-block-start: 1px solid var(--jt-border); }
  .jt-mod-flag-row .jt-mod-flag-reporter { margin-inline-start: 0; font-size: 0.8125rem; color: var(--jt-text-tertiary); }
  .jt-mod-flag-row .jt-mod-flag-note { display: inline-flex; align-items: center; gap: var(--jt-space-1); font-size: 0.8125rem; color: var(--jt-text-secondary); background: transparent; padding: 0; cursor: help; }
  .jt-mod-flag-action { display: inline-flex; align-items: center; gap: var(--jt-space-1); margin-inline-start: auto; font-size: 0.875rem; font-weight: 500; color: var(--jt-accent); text-decoration: none; }
  .jt-mod-flag-action:hover { text-decoration: underline; }

  /* Sidebar links column — moderation link sits below "View all members". */
  .jt-sidebar-links { display: flex; flex-direction: column; gap: var(--jt-space-2); margin-top: var(--jt-space-3); }
  .jt-sidebar-link-mod { display: inline-flex; align-items: center; gap: var(--jt-space-1); }

  /* Space-members role dropdown — only rendered for space admins. */
  .jt-member-role-select {
    appearance: auto;
    padding: var(--jt-space-1) var(--jt-space-2);
    border: 1px solid var(--jt-border);
    border-radius: var(--jt-radius-sm);
    background: var(--jt-bg);
    color: var(--jt-text);
    font-size: var(--jt-text-sm);
    line-height: 1.4;
    max-width: 8rem;
  }
  .jt-member-role-select:disabled { opacity: .6; cursor: progress; }
  .jt-member-role-select:focus { outline: 2px solid var(--jt-accent); outline-offset: 1px; }
  .jt-member-role-error { flex-basis: 100%; color: var(--jt-danger); font-size: var(--jt-text-xs); margin-top: var(--jt-space-1); }

  @media (max-width: 640px) {
    /* Let dropdown + badges wrap onto a second line on mobile. The row
       keeps avatar + name + trust on line 1; dropdown + badge + rep on
       line 2, full-width select so the truncation stops squeezing. */
    .jt-app .jt-member-item { flex-wrap: wrap; }
    .jt-member-role-select { flex-basis: 100%; max-width: 100%; width: 100%; margin-top: var(--jt-space-1); }
    .jt-pending-actions { flex-basis: 100%; margin-top: var(--jt-space-1); }
  }

  /* ── Search ── */
  /* ── Search page form (separate from header search) ── */
  .jt-search-page-form { margin-bottom: 24px; }
  .jt-search-page-input { display: flex !important; align-items: center; gap: 10px; width: 100%; padding: 14px 20px; border: 2px solid var(--jt-border); border-radius: var(--jt-radius-lg); background: var(--jt-bg); transition: border-color 0.2s, box-shadow 0.2s; }
  .jt-search-page-input:focus-within { border-color: var(--jt-accent); box-shadow: 0 0 0 3px var(--jt-accent-muted); }
  .jt-search-page-icon { flex-shrink: 0; color: var(--jt-text-tertiary); }
  .jt-search-page-input input { flex: 1; border: none !important; background: none !important; outline: none !important; padding: 0 !important; margin: 0 !important; font-family: inherit; font-size: inherit; color: var(--jt-text); box-shadow: none !important; }
  .jt-search-page-input input::placeholder { color: var(--jt-text-tertiary); }
  /* Same uppercase-label treatment as .jt-card h4 — same 12px gap contract.
     .jt-app prefix so the margin survives the .jt-app h3 reset. */
  .jt-app .jt-section-label { font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--jt-text-tertiary); margin-bottom: 12px; }
  .jt-search-result-count { color: var(--jt-text-tertiary); }

  /* ── Tag page ── */
  .jt-tag-hero { font-size: 1.125rem; padding: 4px 14px; }
  .jt-tag-count-label { color: var(--jt-text-tertiary); }

  /* ── Profile page extras ── */
  .jt-profile-bio { font-size: 0.9375rem; color: var(--jt-text-secondary); margin-top: 8px; line-height: 1.6; }
  .jt-profile-meta { font-size: 0.8125rem; display: flex; gap: 16px; margin-top: 10px; color: var(--jt-text-tertiary); }
  .jt-section-heading { font-family: var(--jt-font-heading); font-size: 1.125rem; font-weight: 700; margin-bottom: 12px; }

  /* ── Reply card extras ── */
  .jt-reply-author { }
  .jt-reply-op { font-size: 0.9375rem; color: var(--jt-text-secondary); font-weight: 600; }
  .jt-reply-author { font-weight: 600; font-size: 0.9375rem; line-height: 1.4; color: var(--jt-text); text-decoration: none; }
  .jt-reply-author:hover { text-decoration: underline; color: var(--jt-text); }
  .jt-reply-time { color: var(--jt-text-tertiary); font-size: 0.9375rem; line-height: 1.4; white-space: nowrap; }
  /* Timestamp doubles as the reply's permalink (Discourse/Reddit/GitHub
     convention) — the only way to share a link to a single reply. It must
     keep reading as METADATA, not a body link, so colour/underline are
     explicitly reset: themes style `.jt-app a` aggressively and would
     otherwise paint it accent-blue and underlined. Underline appears on
     hover/focus only, which is where the affordance is needed. */
  .jt-reply-permalink { color: inherit; text-decoration: none; border-radius: var(--jt-radius-sm); }
  .jt-reply-permalink:visited { color: inherit; }
  .jt-reply-permalink:hover, .jt-reply-permalink:focus-visible { color: var(--jt-text-secondary); text-decoration: underline; }
  .jt-reply-permalink:focus-visible { outline: 2px solid var(--jt-accent); outline-offset: 2px; }
  .jt-reply-edit { color: var(--jt-text-tertiary); margin-left: auto; }

  /* ── Time stat on post rows ── */
  .jt-row-time { font-size: 0.9375rem; color: var(--jt-text-secondary); }

  /* ── Category page header ── */
  .jt-cat-page-row { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
  .jt-cat-page-emoji { font-size: 1.75em; }
  .jt-cat-page-desc { color: var(--jt-text-secondary); margin-top: 4px; }

  /* ── Space members ── */
  .jt-member-item { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--jt-border); }
  .jt-member-name { font-weight: 600; color: var(--jt-text); }
  .jt-member-joined { font-size: 0.9375rem; color: var(--jt-text-tertiary); margin-top: 2px; }
  .jt-member-rep { font-family: var(--jt-font-mono); font-size: 0.9375rem; font-weight: 600; color: var(--jt-text-secondary); }
  .jt-member-rep-label { font-weight: 400; color: var(--jt-text-tertiary); }
  .jt-member-sub { color: var(--jt-text-tertiary); margin-top: 2px; }
  .jt-pending-requests { margin-bottom: 20px; }
  .jt-pending-requests-title { display: flex; align-items: center; gap: 8px; margin: 0 0 4px; padding: 12px 16px 0; font-size: 1rem; font-weight: 600; color: var(--jt-text); }
  .jt-pending-item:last-child { border-bottom: none; }
  .jt-pending-message { font-size: 0.875rem; color: var(--jt-text-secondary); margin-top: 2px; }
  .jt-pending-actions { display: flex; gap: 8px; flex-shrink: 0; }

  /* ── Space invite links (space admins only) ── */
  .jt-invite-panel { margin-bottom: var(--jt-space-5); padding: var(--jt-space-4); }
  .jt-invite-panel-title { margin: 0 0 var(--jt-space-1); font-size: var(--jt-text-lg); font-weight: 600; color: var(--jt-text); }
  .jt-invite-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--jt-space-3); margin-top: var(--jt-space-3); }
  .jt-invite-field { display: flex; flex-direction: column; gap: var(--jt-space-1); }
  .jt-invite-label { font-size: var(--jt-text-xs); font-weight: 600; color: var(--jt-text-secondary); }
  .jt-invite-input { min-height: var(--jt-tap); padding: var(--jt-space-2) var(--jt-space-3); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-sm); background: var(--jt-bg); color: var(--jt-text); font-family: var(--jt-font); font-size: var(--jt-text-sm); }
  .jt-invite-input:focus-visible { outline: 2px solid var(--jt-accent); outline-offset: 1px; }
  .jt-invite-error { margin: var(--jt-space-3) 0 0; color: var(--jt-danger); font-size: var(--jt-text-xs); }
  .jt-invite-list { list-style: none; margin: var(--jt-space-4) 0 0; padding: 0; }
  .jt-invite-item { display: flex; align-items: center; gap: var(--jt-space-3); padding: var(--jt-space-3) 0; border-top: 1px solid var(--jt-border); }
  .jt-invite-main { flex: 1; min-width: 0; }
  /* The URL is a 60-char token — `anywhere` so it wraps inside the row
     instead of forcing the card to scroll sideways on a phone.
     direction:ltr is NOT a stray physical property: a URL is LTR content
     whatever the UI direction. Inheriting dir=rtl makes the bidi algorithm
     resolve the trailing "/" (a neutral) to the paragraph direction, so the
     link renders as "/http://site/community/invite/TOKEN" — verified in the
     browser. The clipboard is unaffected (it copies the data attribute), but
     an owner reading or retyping the link off the screen gets a broken URL.
     unicode-bidi:isolate keeps that override from leaking into the RTL row.
     The rtl:ignore is load-bearing: grunt rtlcss flips `direction` by default,
     which rewrote this to direction:rtl in jetonomy-rtl.css and reinstated the
     exact bug in the only build where it bites. */
  /*rtl:ignore*/
  .jt-invite-url { display: block; direction: ltr; unicode-bidi: isolate; font-family: var(--jt-font-mono); font-size: var(--jt-text-xs); color: var(--jt-text); overflow-wrap: anywhere; }
  .jt-invite-meta { display: flex; flex-wrap: wrap; gap: var(--jt-space-1) var(--jt-space-3); margin-top: var(--jt-space-1); font-size: var(--jt-text-xs); color: var(--jt-text-tertiary); }
  .jt-invite-dead { color: var(--jt-danger); font-weight: 600; }
  .jt-invite-actions { display: flex; gap: var(--jt-space-2); flex-shrink: 0; }
  /* Revoke is destructive, so it reads danger rather than neutral-ghost. The
     !important is not optional here: `.jt-app button` above is a blanket
     `color: inherit !important` reset against host themes, and every intentional
     surface has to re-assert past it (see the jt-btn-fill/ghost pair). Hover is
     re-asserted separately because .jt-app button.jt-btn-ghost:hover is one
     specificity step higher and would otherwise pull the red back to neutral. */
  .jt-app button.jt-invite-revoke { color: var(--jt-danger) !important; }
  .jt-app button.jt-invite-revoke:hover, .jt-app button.jt-invite-revoke:focus-visible { color: var(--jt-danger) !important; background-color: var(--jt-danger-light) !important; }
  .jt-invite-empty { margin: var(--jt-space-4) 0 0; color: var(--jt-text-tertiary); font-size: var(--jt-text-sm); }

  @media (max-width: 640px) {
    /* The URL needs the full row width on a phone; drop the actions onto
       their own line and let each input fill the width so the number and
       date pickers are thumb-sized rather than squeezed side by side. */
    .jt-invite-item { flex-wrap: wrap; }
    .jt-invite-main { flex-basis: 100%; }
    .jt-invite-actions { flex-basis: 100%; margin-top: var(--jt-space-2); }
    .jt-invite-field { flex: 1 1 100%; }
    .jt-invite-input { width: 100%; }
    .jt-invite-generate { width: 100%; justify-content: center; }
  }

  /* ── Space roadmap extras ── */
  .jt-idea-excerpt { font-size: 0.9375rem; color: var(--jt-text-tertiary); margin-top: 4px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .jt-kanban-empty { font-size: 0.9375rem; color: var(--jt-text-tertiary); text-align: center; padding: 20px 0; }

  /* ── Edit profile ── */
  .jt-avatar-row { display: flex; align-items: center; gap: 12px; }
  .jt-avatar-round { border-radius: 50%; object-fit: cover; }

  /* Avatar square-crop dialog (1.5.0) */
  .jt-crop-dialog {
    border: 1px solid var(--jt-border);
    border-radius: var(--jt-radius-lg, 12px);
    background: var(--jt-surface, var(--jt-bg));
    color: var(--jt-text);
    padding: var(--jt-space-xl, 20px);
    max-width: 340px;
    width: calc(100vw - 32px);
    box-shadow: var(--jt-shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.2));
  }
  .jt-crop-dialog::backdrop { background: rgba(0, 0, 0, 0.55); }
  .jt-crop-dialog__title { margin: 0 0 4px; font-size: 1.0625rem; }
  .jt-crop-dialog__hint { margin: 0 0 12px; font-size: 0.8125rem; color: var(--jt-text-secondary); }
  .jt-crop-viewport {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    border-radius: var(--jt-radius-md, 8px);
    background: var(--jt-bg-subtle, var(--jt-bg));
    cursor: grab;
    touch-action: none;
  }
  .jt-crop-viewport:active { cursor: grabbing; }
  .jt-crop-viewport img {
    position: absolute;
    top: 50%;
    left: 50%;
    max-width: none;
    user-select: none;
    pointer-events: none;
  }
  .jt-crop-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.35);
    pointer-events: none;
  }
  .jt-crop-zoom { display: block; margin: 14px 0 16px; }
  .jt-crop-zoom input[type='range'] { width: 100%; accent-color: var(--jt-accent); }
  .jt-crop-dialog__actions { display: flex; justify-content: flex-end; gap: 8px; }
  .jt-avatar-hint { font-size: 0.8125rem; color: var(--jt-text-tertiary); }
  .jt-avatar-controls { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
  .jt-avatar-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
  /* .jt-btn's display beats the UA [hidden] rule — make hidden stick. */
  .jt-avatar-buttons button[hidden] { display: none; }

  /* ── Composer login ── */
  .jt-editor-login { text-align: center; padding: 20px; }

  /* ── Narrow container ── */
  .jt-narrow { max-width: 860px; margin-inline: auto; }
  .jt-narrower { max-width: 600px; }

  /* ── Card flush (no padding, overflow hidden) ── */
  .jt-card-flush { padding: 0; overflow: hidden; }

  /* ── Forms ── */
  .jt-form-group { margin-bottom: 16px; }
  .jt-label { display: block; font-size: 0.9375rem; font-weight: 600; color: var(--jt-text); margin-bottom: 6px; }
  .jt-label-hint { font-size: 0.8125rem; font-weight: 400; color: var(--jt-text-tertiary); }
  /* Themes like Reign ship aggressive input[type="text"] color/background rules
     that beat .jt-input on specificity. Anchor color + background + caret to
     the design tokens with !important so dark mode reads correctly under any
     active theme. Same pattern the border / box-shadow lines already use. */
  /* Custom-fields section header on the front-end Edit Profile page. Pro's
     custom-fields extension renders a wrapper with this title above the
     admin-defined fields. Style as a labelled section divider so the user
     sees "new section" rather than treating field-name labels as a wall of
     headings. Matches the visual weight of .jt-label so the form's flat
     structure is preserved. */
  .jt-app .jt-custom-fields { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--jt-border); }
  /* Bump specificity to .jt-app .jt-custom-fields-title (020): theme typography
     CSS injects an inline rule on the bare .jt-custom-fields-title selector at
     010 specificity, and beats us on source order. The .jt-app prefix takes
     the same scope free already uses for every other tokenised rule. */
  .jt-app .jt-custom-fields-title { font-size: 15px; font-weight: 600; color: var(--jt-text); margin: 0 0 12px; padding: 0; line-height: 1.3; }
  .jt-input { width: 100%; padding: 11px 16px; border: 1.5px solid var(--jt-border-strong) !important; border-radius: var(--jt-radius); font-family: var(--jt-font); font-size: 1rem; color: var(--jt-text) !important; background: var(--jt-bg) !important; caret-color: var(--jt-text); transition: border-color 0.15s, box-shadow 0.15s; box-shadow: none !important; outline: none !important; -webkit-appearance: none; text-align: left; }
  /* Single focus ring (border recolor + box-shadow halo). Text inputs always
     match :focus-visible when focused, so a separate outline here doubled the
     ring — :focus and :focus-visible share one indicator. */
  .jt-input:focus, .jt-input:focus-visible { outline: none !important; border-color: var(--jt-accent) !important; box-shadow: 0 0 0 3px var(--jt-accent-muted) !important; }
  .jt-input::placeholder { color: var(--jt-text-secondary) !important; }
  .jt-select { padding: 11px 16px; border: 1.5px solid var(--jt-border-strong); border-radius: var(--jt-radius); font-family: var(--jt-font); font-size: 1rem; color: var(--jt-text) !important; background: var(--jt-bg) !important; transition: border-color 0.15s, box-shadow 0.15s; cursor: pointer; }
  .jt-select:focus, .jt-select:focus-visible { outline: none; border-color: var(--jt-accent); box-shadow: 0 0 0 3px var(--jt-accent-muted); }
  .jt-form-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; align-items: center; }
  .jt-new-post-form { margin-top: 16px; }
  .jt-post-create-title { font-family: var(--jt-font-heading); font-size: 1.375rem; font-weight: 700; margin: 0; }
  .jt-post-create-subtitle { color: var(--jt-text-secondary); font-size: 1rem; margin: var(--jt-space-2) 0 var(--jt-space-6); }

  /* ── Publish mode split-button ──
     The buttons pick up `.jt-btn { border-radius: var(--jt-radius-full) }`
     from the base rule (higher specificity, `.jt-app .jt-btn`), so the
     joined-split-button shape that lived here before 1.3.6 never actually
     rendered — they always drew as two pills. Instead of fighting the
     base button shape we lean into it: two pills with a visible gap.
     Matches Basecamp 9803889865 ("Post Topic button and dropdown touching
     without proper spacing"). */
  .jt-publish-mode { position: relative; display: inline-flex; gap: 6px; align-items: center; }
  .jt-publish-mode__toggle { min-width: 34px; padding-left: 8px; padding-right: 8px; }
  /* [1.4.3 WS3-B] Positioning owned by jetonomySmartDropdown (position:
     fixed + JS-computed top/left). Keep z-index/border/background only. */
  .jt-publish-mode__menu { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); box-shadow: 0 4px 16px rgba(0,0,0,0.12); min-width: 180px; z-index: 200; overflow: hidden; }
  .jt-publish-mode__menu[hidden] { display: none; }
  .jt-publish-mode__option { display: block; width: 100%; padding: 10px 16px; background: none; border: none; text-align: left; font-size: 0.9375rem; color: var(--jt-text); cursor: pointer; transition: background var(--jt-dur); }
  .jt-publish-mode__option:hover { background: var(--jt-bg-hover); }

  /* ── Schedule panel ── */
  .jt-schedule-panel { margin-top: 16px; padding: 16px; background: var(--jt-bg-subtle); border-radius: var(--jt-radius); border: 1px solid var(--jt-border); }
  .jt-schedule-panel[hidden] { display: none; }
  /* Restore native appearance so date/time inputs open their browser
     picker on click. The base .jt-input rule sets -webkit-appearance: none
     for consistent text-input styling; for date/time inputs that kills the
     native picker affordance on Chromium and leaves Firefox with an inert
     field that never opens on click. Double-class selector (specificity
     0,0,2,0) beats theme/reset stylesheets that target
     `input[type="time"]` directly (specificity 0,0,1,1). */
  .jt-input.jt-input--datetime,
  .jt-input.jt-input--date,
  .jt-input.jt-input--time {
    font-family: var(--jt-font);
    -webkit-appearance: auto;
    -moz-appearance: auto;
    appearance: auto;
    cursor: pointer;
  }
  .jt-datetime-split { display: flex; gap: 12px; align-items: center; }
  .jt-datetime-split .jt-input--date { flex: 1 1 auto; min-width: 0; }
  .jt-datetime-split .jt-input--time { flex: 0 0 auto; width: 140px; }

  /* Hour + minute select pair used in the schedule panel. Renders identically
     on every desktop browser (Firefox included — where native <input type="time">
     offers no popup picker) and maps to iOS/Android native scroll-wheel
     pickers on mobile. */
  .jt-time-split { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
  .jt-time-split .jt-input--time-part {
    width: auto;
    min-width: 68px;
    padding: 11px 10px;
    text-align: center;
    cursor: pointer;
  }
  .jt-time-split-sep { font-weight: 600; color: var(--jt-text-secondary); }
  @media (max-width: 640px) {
    .jt-datetime-split { flex-direction: column; align-items: stretch; gap: 8px; }
    .jt-datetime-split .jt-input--time { width: 100%; }
    .jt-time-split { width: 100%; }
    .jt-time-split .jt-input--time-part { flex: 1 1 auto; min-width: 0; }
  }

  /* ── Draft / scheduled badges on profile ── */
  .jt-badge--draft { background: var(--jt-warn-light); color: var(--jt-warn); font-size: 0.75rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); display: inline-block; margin-left: 8px; vertical-align: middle; }
  .jt-badge--scheduled { background: var(--jt-accent-light); color: var(--jt-accent); font-size: 0.75rem; font-weight: 600; padding: 2px 8px; border-radius: var(--jt-radius-full); display: inline-block; margin-left: 8px; vertical-align: middle; }
  .jt-row--draft { opacity: 0.9; }

  @media (max-width: 640px) {
    .jt-form-actions { flex-wrap: wrap; }
    .jt-publish-mode { width: 100%; }
    .jt-publish-mode__submit { flex: 1; }
    .jt-publish-mode__menu { min-width: unset; }
  }

  /* ── Pagination ── */
  .jt-pagination { text-align: center; margin: 20px 0; }
  .jt-pagination-btn { min-width: 200px; justify-content: center; }

  /* ── Prism.js override for theme consistency ── */
  .jt-app pre[class*="language-"], .jt-app code[class*="language-"] { font-family: var(--jt-font-mono); }
  .jt-dark .jt-app pre[class*="language-"] { filter: invert(1) hue-rotate(180deg); }

  /* ── User profile links ── */
  .jt-user-link { display: inline-flex; align-items: center; gap: 6px; color: inherit; text-decoration: none; }
  .jt-user-link:hover { color: inherit; }
  .jt-user-link:hover .jt-user-name { text-decoration: underline; }
  .jt-user-name { font-weight: 500; }

  /* ── Search result excerpt ── */
  .jt-row-excerpt { color: var(--jt-text-tertiary); margin-top: 4px; line-height: 1.5; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

  /* ── Replies section ── */
  .jt-replies-section { scroll-margin-top: 80px; margin-block-start: var(--jt-space-8); }
  .jt-replies-controls { display: flex; align-items: center; gap: 12px; }
  .jt-replies-status { font-size: 0.9375rem; color: var(--jt-text-tertiary); margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--jt-border); }

  /* Load More button — centered, prominent */
  .jt-load-more { text-align: center; padding: 24px 0; }
  .jt-load-more-btn { width: 100%; max-width: 400px; justify-content: center; padding: 12px 24px; font-size: 0.9375rem; font-weight: 600; border: 2px solid var(--jt-border); border-radius: var(--jt-radius-lg); transition: all 0.2s; }
  .jt-load-more-btn:hover { border-color: var(--jt-accent); background: var(--jt-accent-muted); }
  .jt-load-more-btn:disabled { opacity: 0.6; cursor: wait; }

  /* ── Thread collapse/expand ── */
  .jt-thread-toggle-btn {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 10px; margin: 4px 0 4px 24px;
    border: none; background: none; color: var(--jt-text-tertiary);
    font-size: 0.9375rem; font-weight: 500; cursor: pointer;
    border-radius: var(--jt-radius-full);
    transition: all 0.2s;
    font-family: var(--jt-font);
  }
  .jt-thread-toggle-btn:hover { color: var(--jt-text-secondary); background: var(--jt-bg-muted); }

  .jt-thread-children {
    overflow: hidden;
    max-height: 5000px;
    transition: max-height 0.4s ease, opacity 0.3s ease;
    opacity: 1;
  }
  .jt-thread-children.collapsed {
    max-height: 0;
    opacity: 0;
    margin: 0;
  }

  /* ── Reply entrance animation — removed intentionally.
     A `transform` animation with fill-mode: both leaves a retained
     identity-matrix on the computed style (visually `none` but still
     a CSS stacking context), trapping the three-dot dropdown behind
     sibling reply cards. Animating `opacity` alone would still create
     a stacking context while <1. No CSS-only solution avoids the
     stacking trap without losing the entrance polish, so we drop the
     polish — the dropdown behaviour is functional, the animation was
     nice-to-have. Revisit with JS animationend cleanup if reintroduced. */

  /* ── Voting micro-interaction ── */
  .jt-v-btn:active { transform: scale(1.3); }
  .jt-v-num { transition: transform 0.2s ease; }

  /* ── Reply card hover — subtle left accent ──
     Deliberately NOT a `transform`: any non-none transform creates a
     stacking context, which would trap the three-dot dropdown behind
     the next reply card in DOM order (post dropdown works because posts
     don't have a hover transform). Left border change is visually
     similar and keeps the card at z-index: auto. */
  .jt-reply { transition: border-color 0.2s, box-shadow 0.15s ease; }
  .jt-reply:hover { box-shadow: inset 2px 0 0 var(--jt-accent); }

  /* ── New replies indicator banner ── */
  .jt-new-replies-banner {
    position: sticky; bottom: 20px; z-index: 50;
    display: flex; align-items: center; justify-content: center;
    margin: 16px auto; max-width: 300px;
    padding: 10px 20px; border-radius: var(--jt-radius-full);
    background: var(--jt-accent); color: var(--jt-accent-fg);
    font-weight: 600; cursor: pointer;
    box-shadow: 0 4px 16px rgba(0,0,0,0.15);
    animation: jt-bounceIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
    transition: transform 0.2s;
  }
  .jt-new-replies-banner:hover { transform: scale(1.05); }
  @keyframes jt-bounceIn {
    from { opacity: 0; transform: scale(0.8) translateY(20px); }
    to { opacity: 1; transform: scale(1) translateY(0); }
  }

  /* ── Infinite scroll sentinel ── */
  .jt-scroll-sentinel { height: 1px; margin-top: -1px; }

  /* ── Gap loader line with pulse ── */
  .jt-load-gap::before {
    animation: jt-pulse 2s ease infinite;
  }
  @keyframes jt-pulse {
    0%, 100% { opacity: 0.3; }
    50% { opacity: 1; }
  }

  /* ── Replying-to indicator slide in ── */
  .jt-replying-to {
    animation: jt-slideDown 0.3s cubic-bezier(0.22, 1, 0.36, 1);
  }
  @keyframes jt-slideDown {
    from { opacity: 0; transform: translateY(-8px); max-height: 0; padding: 0 14px; }
    to { opacity: 1; transform: translateY(0); max-height: 60px; padding: 8px 14px; }
  }

  /* ── Post actions — hover reveal on pointer devices only ─────────
     On touch devices there's no hover, so always show full opacity
     or the `…` / Share / Bookmark buttons look disabled. Use
     `hover: hover` media feature to gate the reveal effect. */
  .jt-post-foot .jt-act { opacity: 1; transition: opacity 0.15s; }
  @media (hover: hover) and (pointer: fine) {
    .jt-post-foot .jt-act { opacity: 0.72; }
    .jt-post-foot:hover .jt-act,
    .jt-post-foot .jt-act:hover,
    .jt-post-foot .jt-act:focus-visible { opacity: 1; }
    .jt-post-foot .jt-act:first-child { opacity: 1; }
  }

  /* ── Smooth focus transitions ── */
  .jt-editor-body:focus { box-shadow: inset 0 0 0 1px var(--jt-accent-muted); }

  /* ── Reply depth visual indicator (colored thread lines) ── */
  .jt-nested-1::before { background: var(--jt-accent); opacity: 0.3; }
  .jt-nested-2::before { background: var(--jt-success); opacity: 0.3; }
  .jt-nested-3::before { background: var(--jt-warn); opacity: 0.3; }

  /* ═══════════════════════════════════════════
     PREMIUM UX — Uniform Across All Pages
     Entrance animations, hover states, micro-interactions
     ═══════════════════════════════════════════ */

  /* ── 1. Entrance Animations — staggered fade-in ── */

  /* Topic list rows */
  .jt-topics > a:nth-child(1) { animation: jt-slideUp 0.3s ease both; animation-delay: 0s; }
  .jt-topics > a:nth-child(2) { animation: jt-slideUp 0.3s ease both; animation-delay: 0.03s; }
  .jt-topics > a:nth-child(3) { animation: jt-slideUp 0.3s ease both; animation-delay: 0.05s; }
  .jt-topics > a:nth-child(4) { animation: jt-slideUp 0.3s ease both; animation-delay: 0.07s; }
  .jt-topics > a:nth-child(5) { animation: jt-slideUp 0.3s ease both; animation-delay: 0.09s; }
  .jt-topics > a:nth-child(n+6) { animation: jt-slideUp 0.3s ease both; animation-delay: 0.1s; }

  /* Search results (div-wrapped rows) */
  .jt-topics > div { animation: jt-slideUp 0.3s ease both; }

  /* Space cards */
  .jt-space-grid > * { animation: jt-slideUp 0.3s ease both; }
  .jt-space-grid > :nth-child(1) { animation-delay: 0s; }
  .jt-space-grid > :nth-child(2) { animation-delay: 0.05s; }
  .jt-space-grid > :nth-child(3) { animation-delay: 0.1s; }
  .jt-space-grid > :nth-child(4) { animation-delay: 0.12s; }
  .jt-space-grid > :nth-child(5) { animation-delay: 0.14s; }
  .jt-space-grid > :nth-child(n+6) { animation-delay: 0.15s; }

  /* Sidebar cards */
  .jt-sidebar > * { animation: jt-slideUp 0.35s ease both; }
  .jt-sidebar > :nth-child(1) { animation-delay: 0.1s; }
  .jt-sidebar > :nth-child(2) { animation-delay: 0.2s; }
  .jt-sidebar > :nth-child(3) { animation-delay: 0.3s; }

  /* Category sections on home page */
  section { animation: jt-slideUp 0.4s ease both; }

  /* Leaderboard rows */
  .jt-leader { animation: jt-slideUp 0.3s ease both; }

  /* Badge cards */
  .jt-badge-card { animation: jt-slideUp 0.3s ease both; }

  /* ── 2. Hover States — ALL Interactive Elements ── */

  /* Space cards — lift on hover */
  .jt-space-card { transition: border-color 0.2s, transform 0.2s ease, box-shadow 0.2s ease; box-shadow: var(--jt-shadow-sm); }
  .jt-space-card:hover { transform: translateY(-1px); box-shadow: var(--jt-shadow-md); }

  /* Topic rows — lift on hover */
  a.jt-row { transition: box-shadow 0.2s ease, transform 0.2s ease, background 0.15s; }
  a.jt-row:hover { box-shadow: var(--jt-shadow-md); transform: translateY(-1px); }

  /* Leaderboard rows — hover background */
  .jt-leader { transition: background 0.15s; padding: 8px 12px; margin: 0 -12px; border-radius: var(--jt-radius); }
  .jt-leader:hover { background: var(--jt-bg-hover); }

  /* Sidebar trending items — hover background */
  .jt-trend { transition: background 0.15s; padding: 6px 8px; margin: 0 -8px; border-radius: var(--jt-radius); }
  .jt-trend:hover { background: var(--jt-bg-hover); }

  /* Tag pills */
  .jt-tag { transition: all 0.15s; }
  .jt-tag:hover { transform: translateY(-1px); }

  /* Notification items */
  .jt-notif-item { transition: background 0.15s; }
  .jt-notif-item:hover { background: var(--jt-bg-hover); }

  /* Member items */
  .jt-member-item { transition: background 0.15s; }
  .jt-member-item:hover { background: var(--jt-bg-hover); }

  /* User profile links */
  .jt-user-link { transition: opacity 0.15s; }
  .jt-user-link:hover { opacity: 0.8; }

  /* Buttons — press effect */
  .jt-btn:active { transform: scale(0.97); }
  .jt-pill:active { transform: scale(0.95); }

  /* Cards — lift and shadow on hover */
  .jt-card { transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s; box-shadow: var(--jt-shadow-sm); }
  .jt-card:hover { border-color: var(--jt-border-strong); box-shadow: var(--jt-shadow-md); transform: translateY(-1px); }
  /* The notification list is a full-width .jt-card that hosts a position:fixed
     action-menu (SmartDropdown). The hover lift above makes the card a
     containing block for that fixed panel, so the menu anchors to the card
     instead of the viewport — it renders far below the clicked row and shifts
     when the hover state toggles (Basecamp). A full-width list has no use for
     the lift; drop only the transform here, the shadow/border hover stays. */
  .jt-card.jt-notif-list:hover { transform: none; }

  /* ── 3. Community Nav — Active State Animation ── */
  .jt-community-nav-links a { position: relative; }
  .jt-community-nav-links a.active::after {
    content: '';
    position: absolute; bottom: -2px; left: 14px; right: 14px;
    height: 2px; background: var(--jt-accent);
    animation: jt-navUnderline 0.3s ease;
  }
  @keyframes jt-navUnderline {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }
  /* Use ::after underline instead of static border-bottom */
  .jt-community-nav-links a.active { border-bottom-color: transparent; }

  /* ── 4. Page Title / Heading Animation ── */
  @keyframes jt-fadeTitle {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .jt-space-head h1, .jt-post-head h1, .jt-page-title {
    animation: jt-fadeTitle 0.5s ease both;
  }

  /* ── 5. Breadcrumb Trail Animation ── */
  .jt-crumb { animation: jt-fadeIn 0.4s ease both; animation-delay: 0.05s; }
  .jt-crumb a { transition: color 0.15s; }

  /* ── 6. Empty State Animation ── */
  .jt-empty { animation: jt-fadeIn 0.6s ease both; animation-delay: 0.2s; }
  .jt-empty-icon { animation: jt-emptyPulse 3s ease infinite; }
  @keyframes jt-emptyPulse {
    0%, 100% { transform: scale(1); opacity: 0.6; }
    50% { transform: scale(1.05); opacity: 1; }
  }

  /* ── 7. Sort Pills — Active State Transition ── */
  .jt-pill.on, .jt-pill.active {
    transition: background 0.2s, color 0.15s, box-shadow 0.2s;
  }

  /* ── 8. Profile Page Premium ── */

  /* Profile banner gradient shimmer */
  .jt-profile-banner {
    background-size: 200% 200%;
    animation: jt-gradientShift 8s ease infinite;
  }
  @keyframes jt-gradientShift {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
  }

  /* Stats counter — number pop on load */
  .jt-stat-n { animation: jt-countPop 0.5s ease both; animation-delay: 0.3s; }
  @keyframes jt-countPop {
    from { opacity: 0; transform: scale(0.5); }
    70% { transform: scale(1.1); }
    to { opacity: 1; transform: scale(1); }
  }

  /* Badge cards — staggered entrance */
  .jt-badge-card:nth-child(1) { animation-delay: 0.1s; }
  .jt-badge-card:nth-child(2) { animation-delay: 0.15s; }
  .jt-badge-card:nth-child(3) { animation-delay: 0.2s; }
  .jt-badge-card:nth-child(4) { animation-delay: 0.25s; }
  .jt-badge-card:nth-child(5) { animation-delay: 0.3s; }
  .jt-badge-card:nth-child(6) { animation-delay: 0.35s; }

  /* ── 9. Idea Cards (Kanban) Premium ── */
  .jt-idea { transition: all 0.2s ease; }
  .jt-idea:hover { transform: translateY(-3px) rotate(-0.5deg); box-shadow: 0 6px 20px rgba(0,0,0,0.08); }

  /* Kanban columns stagger in */
  .jt-kanban > .jt-col:nth-child(1) { animation: jt-slideUp 0.4s ease both; animation-delay: 0s; }
  .jt-kanban > .jt-col:nth-child(2) { animation: jt-slideUp 0.4s ease both; animation-delay: 0.1s; }
  .jt-kanban > .jt-col:nth-child(3) { animation: jt-slideUp 0.4s ease both; animation-delay: 0.2s; }
  .jt-kanban > .jt-col:nth-child(4) { animation: jt-slideUp 0.4s ease both; animation-delay: 0.3s; }

  /* ── 10. Focus Visible Rings (Accessibility + Premium) ── */
  .jt-app *:focus-visible {
    outline: 2px solid var(--jt-accent);
    outline-offset: 2px;
    border-radius: var(--jt-radius-sm);
  }
  .jt-app *:focus:not(:focus-visible) { outline: none; }

  /* ── 12. Progress Bar Animation ── */
  .jt-progress-fill {
    animation: jt-progressGrow 0.8s ease both;
    animation-delay: 0.3s;
  }
  @keyframes jt-progressGrow {
    from { width: 0; }
  }

  /* ── 11. Reduced Motion — Respect User Preference ── */
  /* Must be last to override all animation rules above */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
    }
  }


/* Mentions */
.jt-mention { color: var(--jt-accent); font-weight: 500; }

/* ── Image Upload: Drag-over state ── */
.jt-editor-dragover { border-color: var(--jt-accent) !important; background: var(--jt-accent-muted); }
.jt-upload-placeholder { padding: 12px; text-align: center; color: var(--jt-text-tertiary); background: var(--jt-bg-muted); border-radius: var(--jt-radius); margin: 8px 0; animation: jt-pulse 1.5s ease infinite; }

/* ── Inline form error (composer, etc.) ──
   Surfaces REST errors (e.g. 403 on invite-only new-post submit) inline above
   the form, independent of any third-party toast library. */
.jt-form-error {
	margin: 0 0 12px;
	padding: 10px 14px;
	background: var(--jt-danger-light);
	color: var(--jt-danger);
	border: 1px solid var(--jt-danger);
	border-radius: var(--jt-radius);
	font-size: 0.9375rem;
	line-height: 1.4;
}
.jt-form-error[hidden] { display: none; }

@media (max-width: 640px) {
	.jt-form-error { padding: 8px 12px; font-size: 0.875rem; }
}

/* ── oEmbed wrappers ── */
.jt-embed,
.jt-link-embed { margin: 12px 0; border-radius: var(--jt-radius); overflow: hidden; }
.jt-embed iframe,
.jt-link-embed iframe { max-width: 100%; border-radius: var(--jt-radius); }
.jt-embed .twitter-tweet,
.jt-link-embed .twitter-tweet { margin: 0 !important; }

/* Provider blockquote reset — themes (BuddyX, Reign, Twenty Twenty-*, etc.)
   apply a default <blockquote> treatment with a left border, italic text,
   and a tinted background. TikTok / Instagram / Twitter use a <blockquote>
   container as their SEO fallback before the embed script hydrates, so the
   theme's quote chrome bleeds onto the embed. Reset hard inside our content
   wrappers AND on the bare element so it works regardless of which wrapper
   the renderer chose (.jt-embed, .jt-link-embed, .jt-post-body, .jt-reply-body). */
.jt-embed .tiktok-embed,
.jt-embed .instagram-media,
.jt-embed .twitter-tweet,
.jt-link-embed .tiktok-embed,
.jt-link-embed .instagram-media,
.jt-link-embed .twitter-tweet,
.jt-post-body blockquote.tiktok-embed,
.jt-post-body blockquote.instagram-media,
.jt-post-body blockquote.twitter-tweet,
.jt-reply-body blockquote.tiktok-embed,
.jt-reply-body blockquote.instagram-media,
.jt-reply-body blockquote.twitter-tweet {
	border: 0 !important;
	border-left: 0 !important;
	background: transparent !important;
	padding: 0 !important;
	font-style: normal !important;
	color: inherit !important;
	quotes: none !important;
	margin-left: auto !important;
	margin-right: auto !important;
	box-shadow: none !important;
}
.jt-embed .tiktok-embed::before,
.jt-embed .tiktok-embed::after,
.jt-embed .instagram-media::before,
.jt-embed .instagram-media::after,
.jt-embed .twitter-tweet::before,
.jt-embed .twitter-tweet::after,
.jt-link-embed .tiktok-embed::before,
.jt-link-embed .tiktok-embed::after,
.jt-link-embed .instagram-media::before,
.jt-link-embed .instagram-media::after,
.jt-link-embed .twitter-tweet::before,
.jt-link-embed .twitter-tweet::after,
.jt-post-body blockquote.tiktok-embed::before,
.jt-post-body blockquote.tiktok-embed::after,
.jt-post-body blockquote.instagram-media::before,
.jt-post-body blockquote.instagram-media::after,
.jt-post-body blockquote.twitter-tweet::before,
.jt-post-body blockquote.twitter-tweet::after,
.jt-reply-body blockquote.tiktok-embed::before,
.jt-reply-body blockquote.tiktok-embed::after,
.jt-reply-body blockquote.instagram-media::before,
.jt-reply-body blockquote.instagram-media::after,
.jt-reply-body blockquote.twitter-tweet::before,
.jt-reply-body blockquote.twitter-tweet::after {
	content: none !important;
}

.jt-embed .tiktok-embed,
.jt-link-embed .tiktok-embed,
.jt-post-body blockquote.tiktok-embed,
.jt-reply-body blockquote.tiktok-embed { min-width: 0; max-width: 605px; }
.jt-embed .instagram-media,
.jt-link-embed .instagram-media,
.jt-post-body blockquote.instagram-media,
.jt-reply-body blockquote.instagram-media { max-width: 540px; }
.jt-embed .twitter-tweet,
.jt-link-embed .twitter-tweet,
.jt-post-body blockquote.twitter-tweet,
.jt-reply-body blockquote.twitter-tweet { max-width: 550px; }

.jt-embed .tiktok-embed iframe,
.jt-embed .instagram-media iframe,
.jt-embed .twitter-tweet iframe,
.jt-link-embed .tiktok-embed iframe,
.jt-link-embed .instagram-media iframe,
.jt-link-embed .twitter-tweet iframe {
	max-width: 100%;
	width: 100% !important;
}

@media (max-width: 640px) {
	.jt-embed .tiktok-embed,
	.jt-embed .instagram-media,
	.jt-embed .twitter-tweet,
	.jt-link-embed .tiktok-embed,
	.jt-link-embed .instagram-media,
	.jt-link-embed .twitter-tweet,
	.jt-post-body blockquote.tiktok-embed,
	.jt-post-body blockquote.instagram-media,
	.jt-post-body blockquote.twitter-tweet {
		max-width: 100%;
		min-width: 0 !important;
	}
}

/* ── Instant Search Dropdown ── */
.jt-search-page-form { position: relative; }
.jt-instant-results { position: absolute; top: 100%; left: 0; right: 0; background: var(--jt-bg); border: 1px solid var(--jt-border); border-top: none; border-radius: 0 0 var(--jt-radius-lg) var(--jt-radius-lg); box-shadow: 0 8px 24px rgba(0,0,0,0.1); z-index: 100; max-height: 400px; overflow-y: auto; }
.jt-instant-result-item { display: block; padding: 12px 18px; border-bottom: 1px solid var(--jt-border); transition: background 0.1s; color: inherit; text-decoration: none; }
.jt-instant-result-item:last-child { border-bottom: none; }
.jt-instant-result-item:hover { background: var(--jt-bg-hover); }
.jt-instant-result-item strong { display: block; font-weight: 600; margin-bottom: 2px; }
.jt-instant-result-item span { color: var(--jt-text-tertiary); font-size: 0.9375rem; }

/* ── G5: Quote Button ── */
.jt-quote-btn { position: absolute; z-index: 200; padding: 6px 14px; background: var(--jt-text); color: var(--jt-bg); border: none; border-radius: var(--jt-radius-full); cursor: pointer; font-weight: 600; font-size: 0.9375rem; box-shadow: 0 4px 12px rgba(0,0,0,0.2); transition: transform 0.1s; }
.jt-quote-btn:hover { transform: scale(1.05); }
.jt-quote { border-left: 3px solid var(--jt-accent); padding: 8px 14px; margin: 8px 0; background: var(--jt-bg-subtle); border-radius: 0 var(--jt-radius) var(--jt-radius) 0; color: var(--jt-text-secondary); }
.jt-quote cite { display: block; font-weight: 600; font-size: 0.9375rem; color: var(--jt-text-tertiary); margin-bottom: 4px; font-style: normal; }

/* ── G6: User Hover Cards ── */
.jt-hover-card { position: absolute; z-index: 300; background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); padding: 16px; width: 280px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); animation: jt-fadeIn 0.2s ease; }
.jt-hc-header { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.jt-hc-avatar { border-radius: 50%; }
.jt-hc-name { font-weight: 600; display: block; }
.jt-hc-trust { font-size: 0.9375rem; color: var(--jt-text-tertiary); }
.jt-hc-bio { font-size: 1rem; color: var(--jt-text-secondary); margin: 0 0 8px; line-height: 1.4; }
.jt-hc-stats { display: flex; gap: 16px; font-size: 0.9375rem; color: var(--jt-text-tertiary); font-weight: 500; }

/* ── G8: Keyboard Shortcuts ── */
.jt-kb-focus { outline: 2px solid var(--jt-accent) !important; outline-offset: -2px; }
.jt-shortcut-help { position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; }
.jt-shortcut-modal { background: var(--jt-bg); border-radius: var(--jt-radius-lg); padding: 24px; max-width: 360px; width: 90%; box-shadow: 0 16px 48px rgba(0,0,0,0.2); }
.jt-shortcut-modal h3 { margin: 0 0 16px; }
.jt-shortcut-modal table { width: 100%; }
.jt-shortcut-modal td { padding: 6px 0; }
.jt-shortcut-modal kbd { background: var(--jt-bg-muted); padding: 2px 8px; border-radius: 4px; font-family: var(--jt-font-mono); border: 1px solid var(--jt-border); }
.jt-shortcut-modal button { margin-top: 16px; width: 100%; padding: 8px; border-radius: var(--jt-radius); border: 1px solid var(--jt-border); background: none; cursor: pointer; }

/* ── G9: Emoji Picker ── */
.jt-emoji-picker { position: fixed; background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); padding: 8px; display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); z-index: 9999; width: 220px; }
.jt-emoji-option { border: none; background: none; cursor: pointer; font-size: 1.25rem; line-height: 1; padding: 6px; border-radius: var(--jt-radius-sm); transition: background 0.1s; }
.jt-emoji-option:hover { border: none; background: var(--jt-bg-muted); }

/* ── Gate message inline feedback (JS-toggled classes) ── */
.jt-gate-msg--error   { color: var(--jt-danger); }
.jt-gate-msg--success { color: var(--jt-success); }

/* ── Follow button ── */
/* Scoped to .jt-app so it can actually win. Declared bare (0,1,0) it lost to the
   `.jt-app .jt-btn` base (0,2,0) that sets the normal padding, so the size
   modifier never applied ANYWHERE on the site — measured: asks for 4px 12px,
   rendered 8px 18px. Every "small" button has silently been full-size. Found
   while building the invite panel, whose revoke/copy controls are jt-btn-sm. */
.jt-app .jt-btn-sm { font-size: 0.8125rem; padding: 0.25rem 0.75rem; border-radius: var(--jt-radius); }
.jt-following { background: var(--jt-accent); color: var(--jt-accent-fg); border-color: var(--jt-accent); }
.jt-following:hover { background: var(--jt-danger); border-color: var(--jt-danger); }

/* ── Share dropdown ── */
/* [Basecamp 9808920407] Positioning is owned by view.js::sharePost which
   appends the dropdown to document.body with `position: fixed` and viewport-
   computed top/left. The previous CSS had `position: absolute; bottom: 100%;
   left: 0` which anchored to the dropdown's DOM parent (`.jt-post-foot`)
   instead of the share button itself — so the dropdown rendered ~180px above
   .jt-post-foot, off-screen for posts near the viewport top. Leave only
   visual styling here. */
.jt-share-dropdown { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); box-shadow: 0 4px 16px rgba(0,0,0,0.1); z-index: 100; min-width: 11rem; padding: 0.25rem 0; }
.jt-share-item { display: flex; align-items: center; gap: 0.625rem; width: 100%; text-align: left; padding: 0.5rem 0.75rem; border: none; background: none; color: var(--jt-text); font-size: 0.875rem; font-family: inherit; cursor: pointer; white-space: nowrap; transition: background-color var(--jt-dur, 150ms) var(--jt-ease, ease-out), color var(--jt-dur, 150ms) var(--jt-ease, ease-out); }
.jt-share-item-icon { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; flex: 0 0 16px; color: var(--jt-text-secondary); }
.jt-share-item-icon svg { width: 16px; height: 16px; display: block; }
.jt-share-item-label { display: inline-block; }
.jt-share-item:hover,
.jt-share-item:focus-visible { background: var(--jt-bg-hover); color: var(--jt-accent); }
.jt-share-item:focus-visible { outline: 2px solid var(--jt-accent); outline-offset: -2px; }
.jt-share-item:hover .jt-share-item-icon,
.jt-share-item:focus-visible .jt-share-item-icon { color: var(--jt-accent); }

/* ── Bookmark button ── */
.jt-bookmark-btn.bookmarked { color: var(--jt-accent); }

/* ── Notification preferences grid ── */
.jt-notif-prefs { border: 1px solid var(--jt-border); border-radius: var(--jt-radius); overflow: hidden; }
.jt-notif-optout { display: flex; align-items: flex-start; gap: 0.5rem; margin-top: 0.75rem; font-size: 0.875rem; color: var(--jt-text-secondary); cursor: pointer; }
.jt-notif-optout input { margin-top: 0.15rem; flex-shrink: 0; }
.jt-notif-row { display: grid; grid-template-columns: 1fr 3.5rem 3.5rem; align-items: center; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--jt-border); font-size: 0.875rem; color: var(--jt-text); }
.jt-notif-row:last-child { border-bottom: none; }
.jt-notif-header { font-weight: 600; background: var(--jt-bg-subtle); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--jt-text-secondary); }
.jt-notif-header span:not(:first-child) { text-align: center; }
.jt-toggle { position: relative; display: inline-block; width: 2rem; height: 1.125rem; margin: 0 auto; }
.jt-toggle input { opacity: 0; width: 0; height: 0; }
.jt-toggle-slider { position: absolute; cursor: pointer; inset: 0; background: var(--jt-border-strong); border-radius: var(--jt-radius-full); transition: background var(--jt-dur) var(--jt-ease); }
.jt-toggle-slider::before { content: ''; position: absolute; height: 0.875rem; width: 0.875rem; left: 0.125rem; bottom: 0.125rem; background: var(--jt-bg); border-radius: 50%; transition: transform var(--jt-dur) var(--jt-ease); }
.jt-toggle input:checked + .jt-toggle-slider { background: var(--jt-accent); }
.jt-toggle input:checked + .jt-toggle-slider::before { transform: translateX(0.875rem); }

/* ── Profile tabs ── */
.jt-profile-tabs { display: flex; gap: 0; border-bottom: 2px solid var(--jt-border); margin-bottom: 1rem; }
.jt-profile-tab { padding: 0.5rem 1rem; font-size: 0.875rem; font-weight: 500; color: var(--jt-text-secondary); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -2px; transition: color var(--jt-dur) var(--jt-ease), border-color var(--jt-dur) var(--jt-ease); }
.jt-profile-tab:hover { color: var(--jt-text); }
.jt-profile-tab.active { color: var(--jt-accent); border-bottom-color: var(--jt-accent); }
/* Mobile: the five-tab row (Posts, Replies, Votes, Bookmarks, Drafts)
   overflows the viewport at ≤390px, clipping the last tab (Drafts) off
   the right edge. Make the row horizontally scrollable instead of
   clipping — tabs stay legible, the Drafts tab is reachable by swipe,
   and the underline nav paradigm is preserved. */
@media (max-width: 640px) {
  .jt-profile-tabs {
    overflow-x: auto;
    flex-wrap: nowrap;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .jt-profile-tabs::-webkit-scrollbar { display: none; }
  .jt-profile-tab {
    flex: 0 0 auto;
    white-space: nowrap;
  }
}

/* ── More menu (post action overflow) ── */
.jt-more-menu { position: relative; display: inline-flex; margin-left: 8px; }
/* More-trigger button — was styled for a text `…` character, now
   uses a Lucide more-horizontal SVG. Keep the min tap-target only. */
.jt-more-trigger { min-width: var(--jt-tap); min-height: var(--jt-tap); justify-content: center; }
/* Positioning is owned by jetonomySmartDropdown (WS3-A primitive, wired
   in WS3-B). It sets `position: fixed` and computes top/left so the panel
   flips above when there's no room below and shifts to stay on-screen —
   fixing Basecamp 9886004438 (last-reply More menu clipped). We keep
   z-index/border/background here; everything else is JS-controlled. */
.jt-more-dropdown { background: var(--jt-bg); border: 1px solid var(--jt-border); border-radius: var(--jt-radius); box-shadow: 0 4px 16px rgba(0,0,0,0.1); z-index: 100; min-width: 10rem; padding: 0.25rem 0; }
.jt-more-item { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; padding: 0.5rem 0.75rem; border: none; background: none; color: var(--jt-text); font-size: 0.875rem; cursor: pointer; white-space: nowrap; font-family: var(--jt-font); text-decoration: none; }
.jt-more-item:hover { background: var(--jt-bg-hover); color: var(--jt-text); }
.jt-more-item--danger { color: var(--jt-danger); }
.jt-more-item--danger:hover { background: var(--jt-danger-light); color: var(--jt-danger); }

/* ── Mobile Bottom Tab Bar ── */
.jt-mobile-tabs {
  display: none;
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 999;
  background: var(--jt-bg);
  border-top: 1px solid var(--jt-border);
  padding: 6px 0 env(safe-area-inset-bottom, 0);
  box-shadow: 0 -2px 10px rgba(0,0,0,0.06);
}
@media (max-width: 640px) {
  .jt-mobile-tabs { display: flex; justify-content: space-around; align-items: center; }
  /* Add bottom padding to body so content isn't hidden behind the tab bar */
  .jt-app { padding-bottom: 68px; }
}
.jt-mobile-tab {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 6px 8px; min-width: 56px;
  color: var(--jt-text-tertiary); text-decoration: none;
  font-size: 0.625rem; font-weight: 500;
  transition: color var(--jt-dur);
  position: relative;
}
.jt-mobile-tab svg { display: block; }
.jt-mobile-tab:hover, .jt-mobile-tab.active { color: var(--jt-accent); }
.jt-mobile-tab-badge {
  position: absolute; top: 2px; right: 4px;
  background: var(--jt-danger); color: var(--jt-white);
  font-size: 0.625rem; font-weight: 700;
  min-width: 14px; height: 14px; padding: 0 3px;
  border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ── Toast notification (standalone fallback) ── */
.jt-toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%) translateY(20px);
  z-index: 10001; background: var(--jt-text, #1a1a1a); color: var(--jt-bg, #fff);
  padding: 10px 24px; border-radius: var(--jt-radius-lg, 12px);
  font-size: 0.875rem; font-weight: 600;
  box-shadow: 0 4px 16px rgba(0,0,0,0.2);
  opacity: 0; transition: opacity 0.3s, transform 0.3s;
  pointer-events: none;

  /* This is the shared toast: anything that calls window.bnToast() lands here,
     including third-party messages we do not control the length of (a
     gamification plugin's "You earned 10 points for your first reply!", for
     instance). It used to be `white-space: nowrap` with no max-width, so a
     message of any real length grew WIDER THAN THE VIEWPORT — measured at 394px
     inside a 390px screen — and spilled over the content behind it. Let it wrap
     and cap it to the screen. */
  max-width: min(28rem, calc(100vw - 32px));
  white-space: normal;
  text-align: center;
  overflow-wrap: anywhere;
}
.jt-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ── Custom Modal (replaces browser alert/confirm/prompt) ── */
.jt-modal-overlay {
  position: fixed; inset: 0; z-index: 10000;
  background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center;
  animation: jt-fadeIn 0.15s ease;
}
.jt-modal-box {
  background: var(--jt-bg); border-radius: var(--jt-radius-lg);
  padding: 24px; width: 400px; max-width: calc(100vw - 32px);
  box-shadow: 0 16px 48px rgba(0,0,0,0.2);
}
.jt-modal-msg { font-size: 0.9375rem; line-height: 1.5; margin: 0 0 16px; color: var(--jt-text); }
.jt-modal-input { width: 100%; margin-bottom: 16px; resize: vertical; }
.jt-modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
.jt-modal-actions .jt-btn { min-width: 90px; justify-content: center; }
@media ( max-width: 640px ) {
	.jt-modal-box { padding: 16px; max-width: calc(100vw - 16px); }
	.jt-modal-actions { flex-direction: column-reverse; gap: 6px; }
	.jt-modal-actions .jt-btn { min-width: 0; width: 100%; }
}

/*
 * Merge-picker modal — rebuilt in 1.4.3 to replace the bare prompt-style
 * search box (one heading, one input, one stack of unstyled rows) with a
 * proper search-and-pick experience. Every block is a class so themes and
 * dark mode can override through tokens; no inline styles remain.
 */
.jt-modal-picker { width: 520px; max-width: calc(100vw - 32px); padding: 20px 20px 16px; }
.jt-modal-picker-header { margin-bottom: 12px; }
.jt-modal-picker-title { font-size: 1rem; font-weight: 600; color: var(--jt-text); margin: 0; line-height: 1.4; }
.jt-modal-picker-source { display: flex; align-items: baseline; gap: 6px; margin: 6px 0 0; font-size: 0.8125rem; color: var(--jt-text-secondary); }
.jt-modal-picker-source-label { text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.6875rem; font-weight: 600; color: var(--jt-text-tertiary); }
.jt-modal-picker-source-name { color: var(--jt-text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jt-modal-picker-search { margin-bottom: 10px; }
.jt-modal-picker-input { width: 100%; padding: 8px 12px; font-size: 0.9375rem; border: 1px solid var(--jt-border); border-radius: var(--jt-radius); background: var(--jt-bg); color: var(--jt-text); transition: border-color 0.15s, box-shadow 0.15s; box-sizing: border-box; }
.jt-modal-picker-input:focus { outline: none; border-color: var(--jt-accent); box-shadow: 0 0 0 3px var(--jt-accent-light, rgba(59, 130, 246, 0.18)); }
.jt-modal-picker-results { max-height: 320px; min-height: 80px; overflow-y: auto; border: 1px solid var(--jt-border); border-radius: var(--jt-radius); background: var(--jt-bg); margin-bottom: 16px; }
.jt-modal-picker-results.is-loading { opacity: 0.7; }
.jt-modal-picker-hint { margin: 0; padding: 14px 14px; font-size: 0.8125rem; color: var(--jt-text-tertiary); text-align: center; }
.jt-modal-picker-hint.is-error { color: var(--jt-danger, var(--jt-text-secondary)); }
.jt-modal-picker-item { display: flex; flex-direction: column; gap: 4px; width: 100%; padding: 10px 12px; text-align: start; background: transparent; border: 0; border-bottom: 1px solid var(--jt-border); cursor: pointer; color: var(--jt-text); font: inherit; transition: background-color 0.15s; }
.jt-modal-picker-item:last-child { border-bottom: 0; }
.jt-modal-picker-item:hover { background: var(--jt-bg-hover, var(--jt-bg-muted)); }
.jt-modal-picker-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--jt-accent); }
.jt-modal-picker-item.is-selected { background: var(--jt-accent-light, var(--jt-bg-muted)); }
.jt-modal-picker-item-title { font-size: 0.9375rem; font-weight: 500; color: var(--jt-text); line-height: 1.4; }
.jt-modal-picker-item-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 0.75rem; color: var(--jt-text-tertiary); }
.jt-modal-picker-space-chip { display: inline-flex; padding: 2px 6px; border-radius: var(--jt-radius-sm, 4px); background: var(--jt-bg-muted); color: var(--jt-text-secondary); font-weight: 600; letter-spacing: 0.02em; }
.jt-modal-picker-item-stat { color: var(--jt-text-tertiary); }
@media ( max-width: 640px ) {
	.jt-modal-picker { padding: 16px 16px 12px; }
	.jt-modal-picker-results { max-height: 50vh; }
}

/* ── Link Preview Cards ──
   LinkedIn / Twitter / Facebook style rich card. Media sits on top, metadata
   below — same layout scales from desktop (560px wide) down to mobile (full
   width). Hero media hides automatically when .jt-link-preview--no-media is
   set (image failed to load or provider didn't expose one). */
.jt-link-preview {
  display: flex; flex-direction: column;
  border: 1px solid var(--jt-border); border-radius: var(--jt-radius);
  background: var(--jt-bg);
  overflow: hidden; margin: 12px 0; max-width: 560px;
  text-decoration: none; color: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.jt-link-preview:hover,
.jt-link-preview:focus-visible {
  border-color: var(--jt-accent);
  box-shadow: 0 4px 16px rgba(0,0,0,0.08);
  color: inherit; text-decoration: none;
}
.jt-link-preview-media {
  position: relative;
  width: 100%;
  aspect-ratio: 1.91 / 1;         /* og:image spec: 1.91:1 */
  overflow: hidden; background: var(--jt-bg-subtle);
}
.jt-link-preview-img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.jt-link-preview-body {
  display: flex; flex-direction: column; gap: 6px;
  padding: 14px 16px; min-width: 0;
}
.jt-link-preview-meta {
  display: flex; align-items: center; gap: 6px;
  font-size: 0.75rem; color: var(--jt-text-tertiary);
  text-transform: uppercase; letter-spacing: 0.02em;
}
.jt-link-preview-favicon {
  width: 14px; height: 14px; object-fit: contain; flex-shrink: 0;
  border-radius: 2px;
}
.jt-link-preview-domain { color: var(--jt-text-tertiary); font-weight: 500; }
.jt-link-preview-title {
  font-size: 0.9375rem; font-weight: 600; line-height: 1.35; color: var(--jt-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.jt-link-preview-desc {
  margin: 0; font-size: 0.8125rem; color: var(--jt-text-secondary); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.jt-link-preview-foot {
  display: flex; align-items: center; gap: 8px;
  margin-top: 4px; font-size: 0.75rem; color: var(--jt-text-tertiary);
}
.jt-link-preview-author { font-weight: 500; }
.jt-link-preview-date::before { content: "·"; margin-right: 8px; }

/* Compact, side-by-side layout when there's no hero media — feels closer to
   LinkedIn's small-card and avoids the giant empty box. */
.jt-link-preview--no-media .jt-link-preview-body { padding: 14px 16px; }

/* Third-party embed container (TikTok/Twitter/Instagram blockquote, YouTube
   iframe). The provider's own stylesheet handles internal layout; we only own
   the outer spacing + max-width for mobile. */
.jt-link-embed {
  margin: 12px 0; max-width: 560px;
}
.jt-link-embed iframe,
.jt-link-embed blockquote {
  max-width: 100% !important;
}
.jt-link-embed[data-provider="youtube"] iframe,
.jt-link-embed[data-provider="vimeo"] iframe {
  width: 100%; aspect-ratio: 16 / 9; height: auto;
}

@media (max-width: 640px) {
  .jt-link-preview { max-width: 100%; margin: 10px 0; }
  .jt-link-preview-media { aspect-ratio: 16 / 9; }
  .jt-link-preview-title { font-size: 0.9375rem; }
  .jt-link-embed { max-width: 100%; }
}

/* ── Notification Dropdown Panel ── */
.jt-notif-dropdown-wrap { position: relative; display: inline-flex; }
.jt-notif-panel {
  position: absolute; top: 100%; right: 0; z-index: 200;
  width: 340px; max-height: 420px;
  background: var(--jt-bg); border: 1px solid var(--jt-border);
  border-radius: var(--jt-radius-lg);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  overflow: hidden;
  animation: jt-fadeIn 0.2s ease;
}
.jt-notif-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--jt-border);
}
.jt-notif-panel-head strong { font-size: 0.875rem; }
.jt-notif-mark-read {
  border: none; background: none; color: var(--jt-accent);
  font-size: 0.75rem; font-weight: 600; cursor: pointer; padding: 0;
  font-family: var(--jt-font);
}
.jt-notif-mark-read:hover { border: none; background: none; color: var(--jt-accent); text-decoration: underline; }
.jt-notif-panel-body { max-height: 280px; overflow-y: auto; }
.jt-notif-panel-loading, .jt-notif-panel-empty {
  padding: 24px 16px; text-align: center;
  color: var(--jt-text-tertiary); font-size: 0.875rem;
}
.jt-notif-panel-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 16px; border-bottom: 1px solid var(--jt-border);
  text-decoration: none; color: inherit; transition: background 0.1s;
}
.jt-notif-panel-item:last-child { border-bottom: none; }
.jt-notif-panel-item:hover { background: var(--jt-bg-hover); }
.jt-notif-panel-item.unread { background: var(--jt-accent-light); }
.jt-notif-panel-text { font-size: 0.8125rem; line-height: 1.4; }
.jt-notif-panel-time { font-size: 0.6875rem; color: var(--jt-text-tertiary); }
.jt-notif-panel-footer {
  display: block; text-align: center;
  padding: 10px 16px; border-top: 1px solid var(--jt-border);
  font-size: 0.8125rem; font-weight: 600; color: var(--jt-accent);
  text-decoration: none; transition: background 0.1s;
}
.jt-notif-panel-footer:hover { background: var(--jt-bg-hover); color: var(--jt-accent); }
@media (max-width: 640px) {
  .jt-notif-panel { width: calc(100vw - 32px); right: -16px; }
}

/* ── Search Overlay (Cmd+K) ── */
.jt-search-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(0,0,0,0.4);
  display: none; align-items: flex-start; justify-content: center;
  padding-top: 15vh;
}
.jt-search-overlay.open { display: flex; }
.jt-search-overlay-inner {
  width: 560px; max-width: calc(100vw - 32px);
  background: var(--jt-bg); border-radius: var(--jt-radius-lg);
  box-shadow: 0 16px 48px rgba(0,0,0,0.2);
  overflow: hidden;
  animation: jt-fadeIn 0.15s ease;
}
.jt-search-overlay-field {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 20px; border-bottom: 1px solid var(--jt-border);
}
.jt-search-overlay-icon { flex-shrink: 0; color: var(--jt-text-tertiary); display: flex; }
.jt-search-overlay-input {
  flex: 1; border: none; background: none;
  font-family: var(--jt-font); font-size: 1.0625rem; color: var(--jt-text);
  padding: 0;
}
.jt-search-overlay-input::placeholder { color: var(--jt-text-tertiary); }
.jt-search-overlay-kbd {
  font-family: var(--jt-font-mono); font-size: 0.6875rem;
  padding: 2px 6px; border-radius: 4px;
  background: var(--jt-bg-muted); border: 1px solid var(--jt-border);
  color: var(--jt-text-tertiary);
}
.jt-search-overlay-results { max-height: 360px; overflow-y: auto; }
.jt-search-overlay-empty {
  padding: 24px 20px; text-align: center;
  color: var(--jt-text-tertiary); font-size: 0.875rem;
}
.jt-search-overlay-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 12px 20px; border-bottom: 1px solid var(--jt-border);
  text-decoration: none; color: inherit; transition: background 0.1s;
}
.jt-search-overlay-item:last-child { border-bottom: none; }
.jt-search-overlay-item:hover { background: var(--jt-bg-hover); }
.jt-search-overlay-item strong { font-size: 0.9375rem; }
.jt-search-overlay-item span { font-size: 0.8125rem; color: var(--jt-text-tertiary); }

/* ── Skeleton Loading Placeholders ── */
@keyframes jt-shimmer {
  0% { background-position: -400px 0; }
  100% { background-position: 400px 0; }
}
.jt-skeleton {
  background: linear-gradient(90deg, var(--jt-bg-muted) 25%, var(--jt-bg-subtle) 50%, var(--jt-bg-muted) 75%);
  background-size: 800px 100%;
  animation: jt-shimmer 1.5s infinite linear;
  border-radius: var(--jt-radius-sm);
}
.jt-skeleton-text { height: 0.875rem; margin-bottom: 6px; }
.jt-skeleton-text--sm { width: 40%; }
.jt-skeleton-text--md { width: 65%; }
.jt-skeleton-text--lg { width: 90%; }
.jt-skeleton-circle { border-radius: 50%; }
.jt-skeleton-row {
  display: grid; grid-template-columns: 48px 1fr 80px; gap: 12px; align-items: center;
  padding: 14px 16px; border-bottom: 1px solid var(--jt-border);
}
.jt-skeleton-row .jt-skeleton-vote { width: 32px; height: 32px; border-radius: var(--jt-radius-sm); }
.jt-skeleton-row .jt-skeleton-main { display: flex; flex-direction: column; gap: 6px; }
.jt-skeleton-row .jt-skeleton-stat { width: 40px; height: 24px; border-radius: var(--jt-radius-sm); }

/* Skeleton topic list — 5 placeholder rows */
.jt-skeleton-topics { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); overflow: hidden; }

/* Skeleton reply card */
.jt-skeleton-reply {
  border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg);
  margin-bottom: 10px; overflow: hidden;
}
.jt-skeleton-reply-head { display: flex; align-items: center; gap: 8px; padding: 10px 16px; background: var(--jt-bg-subtle); }
.jt-skeleton-reply-body { padding: 16px 18px; display: flex; flex-direction: column; gap: 6px; }
.jt-skeleton-reply-foot { display: flex; gap: 12px; padding: 8px 16px; }

/* Skeleton sidebar card */
.jt-skeleton-card { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); padding: 16px; margin-bottom: 12px; }
.jt-skeleton-card-title { height: 0.75rem; width: 50%; margin-bottom: 14px; }
.jt-skeleton-card-item { display: flex; align-items: center; gap: 8px; padding: 6px 0; }

/* Skeleton profile */
.jt-skeleton-profile { border: 1px solid var(--jt-border); border-radius: var(--jt-radius-lg); overflow: hidden; margin-bottom: 20px; }
.jt-skeleton-profile-banner { height: 120px; }
.jt-skeleton-profile-body { padding: 0 20px 20px; margin-top: -20px; }
.jt-skeleton-profile-avatar { width: 64px; height: 64px; border: 3px solid var(--jt-bg); margin-bottom: 10px; }
.jt-skeleton-profile-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 16px; padding: 12px; background: var(--jt-bg-subtle); border-radius: var(--jt-radius); }
.jt-skeleton-profile-stat { display: flex; flex-direction: column; align-items: center; gap: 4px; }

/* ── Search filters ── */
.jt-search-filters {
	border: 1px solid var(--jt-border);
	border-radius: var(--jt-radius);
	padding: 0;
	margin-bottom: 1.25rem;
}
.jt-search-filters[open] {
	padding: 0 1rem 1rem;
}
.jt-search-filters-toggle {
	padding: 0.75rem 1rem;
	cursor: pointer;
	font-weight: 600;
	font-size: 0.875rem;
	color: var(--jt-text-secondary);
	display: flex;
	align-items: center;
	gap: 0.5rem;
	list-style: none;
}
.jt-search-filters-toggle::-webkit-details-marker { display: none; }
.jt-search-filters[open] .jt-search-filters-toggle svg {
	transform: rotate(180deg);
}
.jt-filter-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-bottom: 0.75rem;
}
.jt-filter-row label {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	font-size: 0.8125rem;
	color: var(--jt-text-secondary);
	flex: 1;
	min-width: 140px;
}
.jt-filter-actions {
	display: flex;
	gap: 0.5rem;
}
@media (max-width: 640px) {
	.jt-filter-row {
		flex-direction: column;
	}
	.jt-filter-row label {
		min-width: 100%;
	}
}

/* ── Discussion row hover effects (non-anchor rows) ── */
.jt-row { box-shadow: 0 1px 3px rgba(0,0,0,0.04); transition: box-shadow 0.2s ease, transform 0.2s ease, background 0.1s; }
.jt-row:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); transform: translateY(-1px); }
/* Bookmarks page: the per-card "Remove bookmark" toggle (1.5.0). The card is
   a CSS grid, so a plain child lands in an implicit cell — pin it top-right
   instead. Scoped with :has() so only cards that carry the toggle are
   affected (the My Bookmarks list), never every post row. */
.jt-row:has(.jt-bookmark-toggle) { position: relative; }
.jt-bookmark-toggle {
	position: absolute;
	top: 0.5rem;
	inset-inline-end: 0.5rem;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 0;
	background: transparent;
	color: var(--jt-accent);
	cursor: pointer;
	border-radius: var(--jt-radius-sm, 4px);
}
.jt-bookmark-toggle:hover,
.jt-bookmark-toggle:focus-visible { background: var(--jt-bg-hover, rgba(0,0,0,0.05)); }

/* ── Jetonomy Navigation block — Category → Space tree ── */
.jt-nav-block {
	background: var(--jt-bg);
	border: 1px solid var(--jt-border);
	border-radius: var(--jt-radius, 8px);
	padding: 0.75rem 0.9rem;
	font-family: var(--jt-font, inherit);
	color: var(--jt-text);
}
.jt-nav-title {
	font-size: 0.95rem;
	font-weight: 600;
	margin: 0 0 0.75rem;
	color: var(--jt-text);
	letter-spacing: 0.01em;
}
.jt-nav-category {
	margin: 0.35rem 0 0.55rem;
}
.jt-nav-category-title {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--jt-text-tertiary, var(--jt-text-secondary));
	margin: 0.25rem 0 0.25rem;
	font-weight: 600;
	cursor: default;
}
details.jt-nav-category > summary.jt-nav-category-title {
	cursor: pointer;
	list-style: none;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.2rem 0;
}
details.jt-nav-category > summary.jt-nav-category-title::-webkit-details-marker {
	display: none;
}
details.jt-nav-category > summary.jt-nav-category-title::after {
	content: "▸";
	font-size: 0.7rem;
	transition: transform var(--jt-dur, 0.15s) var(--jt-ease, ease);
	color: var(--jt-text-tertiary, var(--jt-text-secondary));
}
details.jt-nav-category[open] > summary.jt-nav-category-title::after {
	transform: rotate(90deg);
}
.jt-nav-spaces {
	list-style: none;
	margin: 0;
	padding: 0;
}
.jt-nav-space {
	margin: 0;
}
.jt-nav-space > a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	padding: 0.4rem 0.55rem;
	border-radius: var(--jt-radius-sm, 6px);
	text-decoration: none;
	color: var(--jt-text);
	font-size: 0.9rem;
	line-height: 1.35;
	transition: background var(--jt-dur, 0.15s) var(--jt-ease, ease), color var(--jt-dur, 0.15s) var(--jt-ease, ease);
}
.jt-nav-space > a:hover,
.jt-nav-space > a:focus-visible {
	background: var(--jt-bg-hover, var(--jt-bg-subtle));
	color: var(--jt-accent, var(--jt-text));
}
.jt-nav-space.is-active > a,
.jt-nav-space > a[aria-current="page"] {
	background: var(--jt-accent-muted, var(--jt-bg-subtle));
	color: var(--jt-accent);
	font-weight: 600;
}
.jt-nav-count {
	font-size: 0.72rem;
	color: var(--jt-text-tertiary, var(--jt-text-secondary));
	background: var(--jt-bg-subtle);
	padding: 0.1rem 0.45rem;
	border-radius: var(--jt-radius-full, 999px);
	min-width: 1.4rem;
	text-align: center;
}
.jt-nav-spaces-uncategorized {
	margin-top: 0.5rem;
	border-top: 1px solid var(--jt-border);
	padding-top: 0.5rem;
}
@media (max-width: 640px) {
	.jt-nav-block {
		padding: 0.6rem;
	}
	.jt-nav-space > a {
		padding: 0.55rem 0.55rem;
		font-size: 0.95rem;
	}
}

@media (max-width: 640px) {
	/* Welcome hero stacks; actions go full-width so the CTA stays tappable. */
	.jt-home-welcome { flex-direction: column; align-items: stretch; }
	.jt-home-welcome-actions { width: 100%; }
	.jt-home-welcome-actions .jt-btn { flex: 1 1 auto; justify-content: center; }
}

/* ═══════════════════════════════════════════════════════════
   Mobile overflow safeguards
   Defensive rules so user-supplied content (long URLs, large
   images, wide tables / code blocks) cannot push the Jetonomy
   app wider than the viewport. Covers customer reports of
   horizontal scroll on small screens (390px / 414px) and is
   theme-agnostic: works regardless of which parent theme
   wraps the community.
   ═══════════════════════════════════════════════════════════ */

.jt-app {
	/* A wide child must not widen the forum. Themes that want
	   a floating element to escape can opt out on their own. */
	max-width: 100vw;
	overflow-x: clip;
}

.jt-post-body,
.jt-reply-body {
	/* Long URLs and unspaced strings (e.g. transaction hashes)
	   must wrap instead of stretching the container. */
	overflow-wrap: anywhere;
	word-break: break-word;
}

.jt-post-body img,
.jt-reply-body img {
	max-width: 100%;
	height: auto;
}

.jt-post-body table,
.jt-reply-body table {
	/* Tables with many columns get their own horizontal scroll
	   inside the post body; the rest of the page stays put. */
	display: block;
	max-width: 100%;
	overflow-x: auto;
}

.jt-post-body iframe,
.jt-reply-body iframe,
.jt-embed iframe {
	max-width: 100%;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Attachments (free)
   Files render UNDER the post/reply, never inside the body — the body is never
   rewritten to show a file. Pro replaces this strip with a richer one (lightbox,
   inline PDF preview); the stored data is identical, so a site that loses Pro
   keeps showing its files, just more plainly.
   ───────────────────────────────────────────────────────────────────────────── */
.jt-attachments {
	list-style: none;
	margin: var(--jt-radius) 0 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--jt-radius-sm);
}

.jt-attachment {
	margin: 0;
	min-width: 0; /* let long file names shrink instead of overflowing the row */
}

.jt-attachment--image {
	flex: 0 1 auto;
}

.jt-attachment__preview {
	display: block;
	border-radius: var(--jt-radius);
	overflow: hidden;
	border: 1px solid var(--jt-border);
	line-height: 0;
}

.jt-attachment__preview img {
	max-width: 100%;
	height: auto;
	max-height: 22rem;
	object-fit: cover;
}

/* Anything that is not an image: name, size, type — enough to decide whether to
   click before downloading. */
.jt-attachment__file {
	display: flex;
	align-items: center;
	gap: var(--jt-radius-sm);
	padding: var(--jt-radius-sm) var(--jt-radius);
	border: 1px solid var(--jt-border);
	border-radius: var(--jt-radius);
	background: var(--jt-bg-subtle);
	color: var(--jt-text);
	text-decoration: none;
	transition: background var(--jt-dur) var(--jt-ease), border-color var(--jt-dur) var(--jt-ease);
}

.jt-attachment__file:hover,
.jt-attachment__file:focus-visible {
	background: var(--jt-bg-hover);
	border-color: var(--jt-border-strong);
	text-decoration: none;
}

.jt-attachment__ext {
	flex: 0 0 auto;
	font-family: var(--jt-font-mono);
	font-size: 0.6875rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	padding: 0.25rem 0.375rem;
	border-radius: var(--jt-radius-sm);
	background: var(--jt-accent-light);
	color: var(--jt-accent);
}

.jt-attachment__meta {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.jt-attachment__name {
	font-size: 0.875rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.jt-attachment__size {
	font-size: 0.75rem;
	color: var(--jt-text-tertiary);
}

@media (max-width: 480px) {
	.jt-attachments {
		flex-direction: column;
	}

	.jt-attachment--file .jt-attachment__file {
		width: 100%;
	}
}
