/* =====================================================================
   Highlight Hub - real brand styling (build pass 2026-09-15).

   Colors match the real logo + static/coming-soon.html exactly (dark
   navy/charcoal background, gold/orange accent, silver "HIGHLIGHT"
   wordmark, black text on the orange "HUB" badge) - see the Business Plan
   vault note's "Still open before this is ready to build" section for the
   source of truth. This replaces the earlier purple placeholder theme.
   ===================================================================== */
:root {
  /* Real rendered height of .topbar - measured live via Playwright (bounding-box read-
     back, not calculated from the padding/logo CSS by hand) for the 2026-10-06 sidebar-
     scroll fix, see the .topbar/.site-sidebar sticky-offset comment below for why this
     has to be exact: 0.9rem vertical padding + the 38px logo + the 1px border-bottom,
     rendered at 70.5625px in Chromium at 100% zoom/1x DPI. */
  --topbar-height: 70.5625px;
  --navy: #10151f;
  --navy-2: #171d2b;
  --navy-3: #1d2436;
  --border: #2a3348;
  --silver: #eef1f5;
  --silver-muted: #9aa3b8;
  --gold: #e8891a;
  --gold-light: #f7b955;
  --gold-text: #10151f;         /* black text on the orange HUB badge */
  --success: #4ade80;
  --success-bg: #10381f;
  --warning: #ffb347;
  --warning-bg: #4a3a10;
  --error: #ff6b6b;
  --error-bg: #3a1414;
  --promo-red: #e8302a;          /* zero-cost clipping promo strikethrough - red isn't used elsewhere on the site */
  --radius: 10px;
  --font: 'Montserrat', 'Segoe UI', system-ui, -apple-system, Roboto, Arial, sans-serif;
}

* { box-sizing: border-box; }

html {
  background: var(--navy);
}
body {
  font-family: var(--font);
  background: radial-gradient(circle at 50% 0%, var(--navy-2), var(--navy) 60%);
  color: var(--silver);
  margin: 0;
  min-height: 100vh;
}

a { color: var(--gold-light); }

/* --------------------------------------------------------------- topbar */
.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.9rem 1.75rem;
  background: rgba(16, 21, 31, 0.9);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
  backdrop-filter: blur(6px);
}
/* Soft gold seam (2026-10-07, per SquirellMaster - the topbar and sidebar met at a plain
   flat-gray right angle, read as two separate boxes jammed together instead of one chrome
   system). A thin glowing gradient line under the ENTIRE topbar, not just a plain border -
   z-index above the sidebar so it draws as one continuous accent across the whole top
   edge, including where the sidebar starts right below it. Pure decoration (::after,
   pointer-events none) - doesn't touch the sticky top:0/--topbar-height math documented
   on .site-sidebar below, which is load-bearing for the scroll-offset fix. */
.topbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(232, 137, 26, 0.7) 12%, rgba(232, 137, 26, 0.35) 50%, transparent 90%);
  pointer-events: none;
}
.brand { text-decoration: none; display: flex; align-items: center; gap: 0.6rem; }
/* Circular "marquee badge" frame around the mark (2026-10-07, per SquirellMaster - the
   bare logo sitting directly on the topbar read as unfinished next to everything else
   that's gotten a themed treatment tonight). Padding+background give the image a coin-
   like disc to sit on regardless of the PNG's own transparent edges, gold ring + soft glow
   matches the neon language used everywhere else (thumb highlight, nav icons, buttons). */
.brand img.logo {
  height: 32px; width: 32px; object-fit: contain; display: block;
  border-radius: 50%; background: var(--navy-2); border: 2px solid var(--gold);
  padding: 4px; box-sizing: border-box;
  box-shadow: 0 0 8px rgba(232, 137, 26, 0.45), inset 0 0 4px rgba(0, 0, 0, 0.4);
}
.brand-word {
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  color: var(--silver);
}
.brand-word .hub-badge {
  background: linear-gradient(135deg, var(--gold-light), var(--gold));
  color: var(--gold-text);
  padding: 0.1rem 0.45rem;
  border-radius: 5px;
  font-weight: 800;
  margin-left: 0.15rem;
}
.who { display: flex; align-items: center; gap: 0.5rem; font-size: 0.86rem; color: var(--silver-muted); }
.who a {
  color: var(--silver-muted);
  text-decoration: none;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.who a:hover {
  color: var(--gold-light);
  border-color: var(--border);
  background: rgba(232, 137, 26, 0.08);
}
.who .nav-identity { display: inline-flex; align-items: center; gap: 0.4rem; text-decoration: none; color: var(--silver-muted); }
.who .nav-identity span { color: var(--silver-muted); }
.who .nav-identity:hover span { text-decoration: underline; }
.nav-avatar {
  width: 26px; height: 26px; border-radius: 50%;
  object-fit: cover; border: 1px solid var(--border); display: block;
}
.who .admin-tag {
  color: var(--gold-text);
  background: var(--gold);
  padding: 0.1rem 0.5rem;
  border-radius: 4px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

main { max-width: 980px; margin: 2rem auto; padding: 0 1.25rem 3rem; }
main.wide { max-width: 1180px; }

/* ------------------------------------------------------ site-wide left sidebar
   New persistent global nav rail (structural pass 2026-10-06) - the 6 primary
   links (Dashboard, Job Updates, Support, Discord Bot, Hub Card, Hubcast) plus
   the admin-only links, all of which used to live inline in .topbar's .who
   cluster, pulled out into their own always-visible rail so .who shrinks down
   to just the bell / bug-report / identity / logout. Visual language mirrors
   .account-rail / .hub-editor-rail (icon + label row, left-accent "active"
   state) but under its own site-sidebar-* prefix - this is a different job:
   a persistent MULTI-PAGE nav (active state resolved server-side off
   request.endpoint in base.html) rather than a single-page JS tab switcher.
   .app-shell/.main-col wrap everything below the topbar in a flex row so the
   sidebar and the existing main/main.wide centering just compose naturally -
   main's own `margin: 0 auto` above still centers it within whatever width
   .main-col (the flex remainder) actually has, no extra margin math needed. */
.app-shell { display: flex; align-items: flex-start; }
.main-col { flex: 1 1 auto; min-width: 0; }

.site-sidebar {
  flex: 0 0 220px;
  width: 220px;
  /* Real bug fix (2026-10-06, per SquirellMaster testing the live sidebar), two parts:
     1) This used to be `top: 0`, same as .topbar below - but .topbar sits ABOVE
        .app-shell in the DOM, so on scroll .topbar reaches its own sticky position
        first and the sidebar (starting further down the page) only catches up to that
        same top:0 line a moment later, visibly overlapping/snapping under the
        now-pinned topbar instead of staying put from the start. Offsetting by the
        topbar's real measured height means the sidebar is already sitting in its final
        resting spot (flush under the topbar) before either one ever needs to move -
        zero visible jump.
     2) This used to also have `align-self: stretch`, which forces the sidebar's own box
        to exactly match .main-col's rendered height. Verified live (Playwright) that this
        silently defeats position:sticky entirely - when a sticky element's own height
        equals its containing block's height, there is zero room for it to be "ahead of"
        the content, so it scrolls 1:1 with the page instead of sticking (confirmed with
        an isolated minimal repro outside this stylesheet, not just a guess). Swapped for
        `min-height: calc(100vh - var(--topbar-height))` instead - the sidebar still
        visually fills at least the viewport's full remaining height (same "colored rail"
        look as before on first paint), but now its box is shorter than the page content,
        which gives sticky positioning the travel room it needs to actually hold the rail
        in place while everything else scrolls underneath it. */
  min-height: calc(100vh - var(--topbar-height));
  position: sticky;
  top: var(--topbar-height);
  z-index: 9;
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  padding: 1.5rem 0.85rem;
  background: rgba(16, 21, 31, 0.55);
  border-right: 1px solid var(--border);
}
/* Glow accent on the right edge, strongest right at the top (2026-10-07, pairs with
   .topbar::after above) - the two gradients overlap right at the corner where the topbar
   and sidebar meet, so that corner reads as one lit seam instead of two flat gray borders
   crossing at a hard right angle. Purely decorative, no layout impact. */
.site-sidebar::after {
  content: "";
  position: absolute;
  top: 0; right: -1px; bottom: 0; width: 2px;
  background: linear-gradient(180deg, rgba(232, 137, 26, 0.6), rgba(232, 137, 26, 0.15) 12%, transparent 35%);
  pointer-events: none;
}
.site-sidebar-group { display: flex; flex-direction: column; gap: 0.2rem; }
.site-sidebar-group-label {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--silver-muted); margin: 0 0 0.3rem 0.6rem;
}
.site-sidebar-link {
  display: flex; align-items: center; gap: 0.7rem;
  color: var(--silver-muted); text-decoration: none; font-weight: 600;
  font-size: 0.88rem; padding: 0.6rem 0.7rem; border-radius: 7px;
  border-left: 3px solid transparent;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  overflow: hidden;
}
.site-sidebar-link:hover { color: var(--silver); background: var(--navy-3); }
.site-sidebar-link.active {
  background: rgba(232, 137, 26, 0.12); border-left-color: var(--gold);
  color: var(--gold-light);
}
/* Nav pulse (2026-10-07) - SquirellMaster wanted the neon-pop language to reach the menu
   too but NOT as a constant glow (too much for text you're staring at all session) - only
   the current page's own link text slowly breathes a glow, and a hover on any link gets
   one quick glow pop instead of a loop, so the rail stays calm until you're actually
   looking right at the active item. */
@keyframes sidebar-link-glow-pulse {
  0%, 100% { text-shadow: 0 0 4px rgba(232, 137, 26, 0.45); }
  50% { text-shadow: 0 0 10px rgba(232, 137, 26, 0.9), 0 0 2px rgba(255, 214, 150, 0.6); }
}
.site-sidebar-link.active .site-sidebar-text {
  animation: sidebar-link-glow-pulse 2.6s ease-in-out infinite;
}
.site-sidebar-link:hover .site-sidebar-text { text-shadow: 0 0 8px rgba(232, 137, 26, 0.7); }
/* Icon glow, no backdrop (2026-10-07 redo, per SquirellMaster - the round chip background
   from the first pass read as its own box sitting behind the icon; he wanted just the
   lit-up highlight with no backdrop shape at all). drop-shadow (not box-shadow) glows the
   actual glyph pixels since there's no box to cast a shadow from. */
.site-sidebar-icon {
  font-size: 1.1rem; line-height: 1; flex-shrink: 0; width: 1.3em; text-align: center;
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform 0.18s ease, filter 0.18s ease;
}
.site-sidebar-link:hover .site-sidebar-icon {
  transform: translateY(-2px) scale(1.1);
  filter: drop-shadow(0 0 6px rgba(232, 137, 26, 0.85));
}
.site-sidebar-link.active .site-sidebar-icon {
  animation: sidebar-icon-glow-pulse 2.6s ease-in-out infinite;
}
@keyframes sidebar-icon-glow-pulse {
  0%, 100% { filter: drop-shadow(0 0 3px rgba(232, 137, 26, 0.45)); }
  50% { filter: drop-shadow(0 0 8px rgba(232, 137, 26, 0.9)); }
}
/* Real bug fix (2026-10-07, per SquirellMaster - "SquirellMaster's Home" was visibly
   sticking out past the sidebar's own right edge). "Home"'s label includes the session
   username, which has no length cap, and a flex item defaults to min-width:auto - it will
   NOT shrink below its own text's intrinsic width no matter what the parent's actual
   width is, so a long username pushed this straight through white-space:nowrap and out
   past .site-sidebar-link's edge. min-width:0 lets it actually shrink to fit; text-
   overflow:ellipsis means a long name truncates with "..." instead of overflowing again. */
.site-sidebar-text {
  flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color 0.12s ease;
}
.sidebar-soon-badge {
  flex-shrink: 0;
  background: var(--warning-bg); color: var(--warning);
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 0.12rem 0.4rem; border-radius: 4px;
}

/* Profile link's real-avatar icon (2026-10-06) - same .nav-avatar look (round, cover,
   bordered) the topbar identity link already uses, just sized down to sit in this rail's
   1.3em icon column instead of the topbar's own spacing. */
.site-sidebar-icon-avatar { display: flex; align-items: center; justify-content: center; }
.site-sidebar-icon-logo { display: flex; align-items: center; justify-content: center; }
.site-sidebar-logo-img { width: 1.2em; height: 1.2em; object-fit: contain; display: block; }
.site-sidebar-avatar {
  width: 20px; height: 20px; border-radius: 50%;
  object-fit: cover; border: 1px solid var(--border); display: block;
}

/* Icon-only collapse (judgment call, flagged for review): Hub Editor's OWN
   internal tool rail (.hub-editor-layout: 260px rail + flexible Stage) only
   stacks to a single column at max-width:960px. Below that, this sidebar
   coexisting with it is moot - everything's already stacked. Between ~960px
   and ~1480px of total viewport width, though, both rails would otherwise be
   fighting Hub Editor's Stage for room (a real triple-column squeeze). Instead
   of touching Hub Editor's own layout, this collapses THIS rail to icons-only
   in that window, handing back ~160px without any changes to hub_editor.html
   or its CSS. Tooltip text comes from each link's `title` attribute. */


/* ---------------------------------------------------------------- cards */
/* Darker card backing (2026-10-07, per SquirellMaster - the old rgba(23,29,43,0.75)
   (navy-3, lighter than the page itself) read as a washed-out gray once he compared it
   directly against the page background. Switched to the page's own darkest navy at higher
   opacity so every card sits closer to the page color instead of visibly lighter than it -
   site-wide, since every .card everywhere shares this one rule. */
.card {
  background: rgba(16, 21, 31, 0.88);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  margin-bottom: 1.5rem;
}
.card.center { text-align: center; }
.card.narrow { max-width: 440px; margin-left: auto; margin-right: auto; }

h1, h2, h3 { margin-top: 0; color: var(--silver); }
.subtle { color: var(--silver-muted); font-size: 0.9rem; }
.hint { color: var(--silver-muted); font-size: 0.8rem; margin-top: 0.25rem; }

/* ----------------------------------------------------------------- home */
/* Theater-marquee hero redo (2026-10-07, SquirellMaster's "movie theater pop" ask) - the
   old hero was flat centered text on the plain page background, nothing to look at before
   you hit the copy. A soft blurred gold radial glow behind the headline (::before, blurred
   via its own box-shadow spread rather than filter:blur so it stays cheap to paint) gives
   it a spotlit marquee feel without a literal image asset. */
.hero {
  text-align: center;
  padding: 3.5rem 1rem 2.5rem;
  position: relative;
}
.hero::before {
  content: "";
  position: absolute;
  top: 0.5rem; left: 50%;
  width: 640px; height: 320px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(232, 137, 26, 0.22), rgba(232, 137, 26, 0) 70%);
  pointer-events: none;
  z-index: 0;
}
.hero > * { position: relative; z-index: 1; }
.hero img.logo-big { max-width: 420px; width: 90%; height: auto; margin-bottom: 0.5rem; }
.hero .tagline { font-style: italic; color: var(--silver-muted); font-size: 1.05rem; margin-bottom: 1.75rem; }
.hero .lead { max-width: 620px; margin: 0 auto 2rem; color: #cfd5de; line-height: 1.6; }
.hero .cta-row { display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; }

.login-spotlight { display: flex; justify-content: center; align-items: flex-start; gap: 1.5rem; flex-wrap: wrap; margin: 0 0 2rem; }
.login-card {
  margin: 0;
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(232, 137, 26, 0.15), 0 12px 32px rgba(0, 0, 0, 0.35);
}
.login-card h2 { margin-bottom: 0.25rem; }
/* Home-page "Request access" card (added 2026-09-23, per SquirellMaster - it needs to be
   "nice and visible," not a small button next to the much larger login card) - shares
   .login-card's exact gold-accent treatment so it reads as an equally prominent second
   path, side by side with Login rather than visually subordinate to it. */
.request-access-home-card { display: flex; flex-direction: column; }
.request-access-home-card .service-list { text-align: left; margin: 0.75rem 0 1.25rem; padding-left: 1.1rem; color: #cfd5de; }
.request-access-home-card .service-list li { margin-bottom: 0.35rem; }

/* Homepage pricing cards (2026-10-30) - replaced the old 24-item "What We Do" category
   grid/tabs with two real subscription tiers, per SquirellMaster's call to stop leading
   with the full a la carte menu and sell actual packages instead. The a la carte catalog
   itself is untouched and still drives the dashboard's own "Request A Service" picker
   (.service-grid/.service-card below, still shared with panel.html) - this only changes
   what the public marketing page leads with. */
.pricing-tiers {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: 1.5rem;
  margin-bottom: 1.5rem;
}
.pricing-card {
  background: var(--navy-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.85rem 1.6rem;
  flex: 1 1 280px;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  position: relative;
}
.pricing-card h3 { font-size: 1.25rem; margin-bottom: 0; }
.pricing-card {
  transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.pricing-card:hover { transform: translateY(-4px); border-color: var(--gold); }
.pricing-card.featured {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(232, 137, 26, 0.15), 0 12px 32px rgba(0, 0, 0, 0.35);
}
.pricing-card.featured:hover {
  box-shadow: 0 0 0 1px rgba(232, 137, 26, 0.3), 0 16px 36px rgba(0, 0, 0, 0.45);
}
.pricing-badge {
  position: absolute;
  top: -0.7rem;
  left: 1.6rem;
  background: linear-gradient(135deg, var(--gold-light), var(--gold));
  color: var(--gold-text);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
}
.pricing-price { font-size: 1.7rem; font-weight: 800; color: var(--gold-light); margin: 0; }
.pricing-price .period { font-size: 0.9rem; font-weight: 600; color: var(--silver-muted); }
.pricing-feature-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.65rem; flex-grow: 1; }
.pricing-feature-list li { display: flex; align-items: flex-start; gap: 0.55rem; color: #cfd5de; font-size: 0.9rem; line-height: 1.45; }
.pricing-feature-list li::before { content: "\2713"; color: var(--gold-light); font-weight: 800; flex-shrink: 0; }
.pricing-soon { color: var(--silver-muted); font-size: 0.82rem; font-style: italic; }
.pricing-custom-card { max-width: 560px; margin-left: auto; margin-right: auto; }

.badge-pill {
  display: inline-block;
  padding: 0.4rem 1.1rem;
  border: 1px solid var(--gold);
  border-radius: 999px;
  color: var(--gold-light);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 0.72rem;
  font-weight: 700;
  margin-bottom: 1.5rem;
  box-shadow: 0 0 10px rgba(232, 137, 26, 0.35), inset 0 0 6px rgba(232, 137, 26, 0.15);
  animation: marquee-badge-glow 2.4s ease-in-out infinite;
}
@keyframes marquee-badge-glow {
  0%, 100% { box-shadow: 0 0 8px rgba(232, 137, 26, 0.3), inset 0 0 6px rgba(232, 137, 26, 0.12); }
  50% { box-shadow: 0 0 16px rgba(232, 137, 26, 0.65), inset 0 0 8px rgba(232, 137, 26, 0.25); }
}

.clip-preview-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.75rem;
  padding: 0.75rem 0;
}
.clip-preview video {
  width: 100%;
  border-radius: var(--radius);
  background: #000;
  display: block;
}
.clip-preview p {
  margin: 0.3rem 0 0;
  font-size: 0.78rem;
  word-break: break-all;
}
.clip-preview-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.45rem;
}
.clip-preview-actions .btn.small {
  font-size: 0.72rem;
  padding: 0.35rem 0.6rem;
}
.clip-preview-actions .btn.small.active {
  border-color: var(--gold);
  background: var(--gold);
  color: #1a1a1a;
}

/* Job detail / clip-browser page (job_detail.html, new 2026-10-06, split out of
   your_jobs.html's grid tiles - see that file's comment for why). .job-detail-header
   reuses the grid tile's own .job-name-wrap/.job-card-meta/.job-card-actions classes
   unmodified; this just adds the header card's own spacing. */
.job-detail-header {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}
.job-detail-name-wrap .job-name-display { font-size: 1.15rem; }

/* The clip browser itself: one big player, an optional thumbnail strip (only rendered
   when a job has more than one clip - a single-clip or bare-file-deliverable job skips
   straight to the player with no strip), and the per-clip action panels underneath
   (server-rendered once per clip, only the active one visible - see job_detail.html's
   inline <script> for the show/hide + src-swap on thumbnail click). */
/* Theater immersion (2026-10-07, per SquirellMaster - "like you're in the theater, viewing
   it" rather than just color accents elsewhere on the page). A dark radial vignette behind
   the player plus two faint vertical glows on either side stand in for dim house lights/
   aisle sconces either side of a screen; the glow on the video itself reads as light
   spilling off a lit screen into a dark room. Kept subtle/gold, not literal red curtains -
   stays in the site's own navy/gold palette instead of introducing a new one.
   REAL BUG fixed 2026-10-07 (SquirellMaster: "background color changed... washed out
   gray"): this `background` declaration was REPLACING .card's own rgba(23,29,43,0.75)
   backing instead of layering on top of it, since both are the same property on the same
   element - this card lost its solid navy backing entirely, letting the page's own lighter
   top-of-page gradient bleed straight through. Multiple comma-separated background layers
   (gradient on top, the original card color last) restores the real card background while
   keeping the vignette as a layer above it, instead of a wholesale replacement. */
.clip-browser {
  margin-bottom: 1.25rem; text-align: center; position: relative;
  background: radial-gradient(ellipse at 50% 38%, rgba(232, 137, 26, 0.07), transparent 65%), rgba(16, 21, 31, 0.75);
  overflow: hidden;
}
.clip-browser::before, .clip-browser::after {
  content: "";
  position: absolute; top: 10%; bottom: 10%; width: 14%;
  background: linear-gradient(180deg, transparent, rgba(232, 137, 26, 0.08) 40%, rgba(232, 137, 26, 0.08) 60%, transparent);
  pointer-events: none; z-index: 0;
}
.clip-browser::before { left: 0; }
.clip-browser::after { right: 0; }
/* Film-reel frame around the player (2026-10-07, per SquirellMaster: "center the video...
   put a film reel border around it where it doesn't interrupt the video but it's outside
   it"). flex+justify-content:center is used instead of relying on the video's own
   margin:auto, since a replaced element whose rendered width shrinks to satisfy
   max-height doesn't reliably self-center that way - explicit flex centering on the
   wrapper is the robust fix, not a guess. Sprocket-hole rows sit in the frame's own
   padding above/below the video, same dot pattern as .feature-reel-card, so the border is
   genuinely OUTSIDE the video box, never drawn on top of its controls. */
.clip-player-frame {
  display: flex;
  justify-content: center;
  position: relative; z-index: 1;
  padding: 14px 0;
}
.clip-player-frame::before, .clip-player-frame::after {
  content: "";
  position: absolute; left: 8%; right: 8%; height: 11px;
  background-image: radial-gradient(circle, var(--navy) 2.6px, transparent 2.7px);
  background-size: 20px 11px; background-repeat: repeat-x; background-position: 10px center;
  pointer-events: none;
}
.clip-player-frame::before { top: 0; }
.clip-player-frame::after { bottom: 0; }
.clip-player-video {
  width: auto;
  max-width: 100%;
  max-height: 70vh;
  border-radius: var(--radius);
  background: #000;
  display: block;
  box-shadow: 0 0 46px 6px rgba(0, 0, 0, 0.55), 0 0 30px rgba(232, 137, 26, 0.18),
    0 0 0 2px rgba(232, 137, 26, 0.35);
}
/* justify-content was "center" (added earlier tonight in a "center everything" pass) -
   REAL BUG, found 2026-10-07 after two rounds of chasing a scroll-restoration theory that
   wasn't it: centering an OVERFLOWING flex row pushes half the overflow into unreachable
   space before the first item, since scrollLeft can never scroll far enough left to reach
   content centered past the container's left edge. This strip almost always overflows (a
   job can have dozens of clips) - flex-start (the default) is the only sane choice for a
   scrolling filmstrip, centering never actually made sense here. The JS scrollLeft-reset
   added for this same bug report stays too - harmless, and real cheap insurance against
   the browser's own scroll restoration on top of this actual fix. */
.clip-thumb-strip {
  display: flex;
  gap: 0.6rem;
  overflow-x: auto;
  padding: 0.75rem 0;
  scrollbar-color: var(--gold) var(--navy-3);
  overflow-anchor: none;
}
/* Themed scrollbar, not the plain default OS gray bar (2026-10-07 "dress this up" pass) -
   scrollbar-color above covers Firefox, the ::-webkit- rules cover Chrome/Edge/Safari. */
.clip-thumb-strip::-webkit-scrollbar { height: 8px; }
.clip-thumb-strip::-webkit-scrollbar-track { background: var(--navy-3); border-radius: 4px; }
.clip-thumb-strip::-webkit-scrollbar-thumb {
  background: linear-gradient(135deg, var(--gold-light), var(--gold)); border-radius: 4px;
}
.clip-thumb-strip::-webkit-scrollbar-thumb:hover { background: var(--gold-light); }
.finishing-tools-label {
  margin: 0.9rem 0 0.4rem; color: var(--gold-light);
  font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
}
.clip-thumb-btn {
  background: none;
  border: none;
  border-radius: 8px;
  padding: 0;
  cursor: pointer;
  flex: none;
  width: 120px;
}
.clip-thumb-btn .job-thumb {
  width: 120px;
  height: 68px;
  border-radius: 8px;
}
/* Selected-clip indicator (2026-10-07 redo) - a flat border box used to sit around this
   with a visible gap (the old .clip-thumb-btn padding), so it never actually hugged the
   thumbnail. Swapped for a glowing outline drawn via box-shadow directly on .job-thumb
   itself (box-shadow isn't clipped by the thumb's own overflow:hidden, so it still reads
   as a tight ring) plus a slow pulse so it reads as "lit up," not just a static box. */
.clip-thumb-btn.active .job-thumb {
  box-shadow: 0 0 0 2px var(--gold-light), 0 0 8px 2px var(--gold), 0 0 16px 5px rgba(232, 137, 26, 0.55);
  animation: clip-thumb-neon-pulse 1.6s ease-in-out infinite;
}
@keyframes clip-thumb-neon-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--gold-light), 0 0 8px 2px var(--gold), 0 0 16px 5px rgba(232, 137, 26, 0.5); }
  50% { box-shadow: 0 0 0 2px var(--gold-light), 0 0 14px 4px var(--gold), 0 0 28px 10px rgba(232, 137, 26, 0.85); }
}
.viral-score-legend { margin: 0 0 0.5rem; font-size: 0.78rem; }
.viral-score-badge {
  display: block;
  width: fit-content;
  margin: 0 auto 0.3rem;
  background: linear-gradient(135deg, #e8701a, #d1264f);
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  font-size: 0.7rem;
  font-weight: 800;
  line-height: 1;
  padding: 0.2rem 0.45rem;
  border-radius: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  letter-spacing: 0.01em;
}
.clip-thumb-label {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.68rem;
  color: var(--silver-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.clip-thumb-btn.active .clip-thumb-label { color: var(--gold-light); }
.clip-action-panel { padding-top: 0.25rem; }
/* Thumbs up/down pop (2026-10-07, per SquirellMaster - these had ZERO dedicated styling
   before this, riding on plain .btn.secondary.small with no visual difference when
   .active got toggled by the JS vote handler - a real feedback gap, not just a "make it
   pop" ask: clicking a vote gave no confirmation it landed. Bigger circular buttons, a
   lift+scale on hover, and a real color (green for up, orange/red for down) once active
   so the client can actually see which way they voted. */
.clip-feedback-btn {
  width: 2.6rem; height: 2.6rem; border-radius: 50%;
  font-size: 1.15rem; padding: 0; display: inline-flex;
  align-items: center; justify-content: center;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.clip-feedback-btn:hover { transform: translateY(-2px) scale(1.08); }
.clip-feedback-btn[data-rating="up"].active {
  background: rgba(74, 222, 128, 0.18); border-color: var(--success); color: var(--success);
  box-shadow: 0 0 14px rgba(74, 222, 128, 0.45);
}
.clip-feedback-btn[data-rating="down"].active {
  background: rgba(255, 107, 107, 0.18); border-color: var(--error); color: var(--error);
  box-shadow: 0 0 14px rgba(255, 107, 107, 0.45);
}

/* Your Jobs card grid (your_jobs.html, replaced the old two-level collapsible
   <details class="job-group"> / <details class="job-row"> accordion here on 2026-10-06,
   per SquirellMaster reviewing the live site - "looks unfinished/unprofessional", asked to
   lean into the thumbnails instead of hiding everything behind dropdowns). Service grouping
   is now a plain always-visible <h2 class="category-heading"> (no more .job-group wrapper);
   each job is a tile in a responsive grid below it.

   Tiles stopped being <details>/<summary> (and stopped expanding inline) later the same
   day, second 2026-10-06 pass: a single AI Clips job can render dozens of clip previews,
   which was swallowing the grid page one tile at a time. Each .job-tile is now a plain div
   with two children: .job-tile-link (a real <a> into that job's own clip-browser page,
   job_detail.html) wrapping the thumbnail + status/date, and .job-tile-controls (a sibling,
   NOT nested inside the <a>) holding the job-name rename UI and the job-card-actions - kept
   apart on purpose so clicking the rename pencil can never also trigger navigation. */
.job-tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1rem;
  margin-bottom: 0.5rem;
}
/* Marquee-tinted card (2026-10-07, same "not just plain outlines" pass as the secondary-
   button tint above) - gold-tinted border instead of flat gray, and a real lift+glow on
   hover instead of the old flat opacity dim, matching the feature-reel/testimonial card
   depth language used elsewhere on the site. */
.job-tile {
  border: 1px solid rgba(232, 137, 26, 0.25);
  border-radius: var(--radius);
  background: rgba(16, 21, 31, 0.75);
  overflow: hidden;
  margin: 0;
  display: flex;
  flex-direction: column;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.job-tile:hover {
  transform: translateY(-2px);
  border-color: rgba(232, 137, 26, 0.55);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(232, 137, 26, 0.2);
}
.job-tile-link {
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(232, 137, 26, 0.18);
}
/* Compounded with .job-thumb (not a bare .job-tile-thumb selector) on purpose - 2026-10-07
   real bug found testing live: this rule sits BEFORE the .job-thumb base rule further down
   this file, and at equal specificity (both were single-class selectors) the later base
   rule's width:112px/height:63px/flex:0 0 auto was silently winning the cascade, so every
   tile thumbnail rendered at the tiny default box instead of this 100%-wide 16:9 one -
   compounding the selector gives this the higher specificity it needs regardless of
   source order, instead of relying on fragile file-position ordering. */
.job-thumb.job-tile-thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  flex: none;
  border-radius: 0;
  border: none;
  position: relative;
}
/* Film-reel sprockets on the tile thumbnail (2026-10-07, same "movie theater pop" pass as
   the clip-browser player frame) - same dot pattern as .feature-reel-card/.clip-player-
   frame, sitting right on the thumbnail's own top/bottom edge since this tile has no
   spare padding around it to frame from outside. */
.job-tile-thumb::before, .job-tile-thumb::after {
  content: "";
  position: absolute; left: 0; right: 0; height: 9px;
  background-image: radial-gradient(circle, var(--navy) 2.2px, transparent 2.3px);
  background-size: 17px 9px; background-repeat: repeat-x; background-position: 8px center;
  z-index: 1; pointer-events: none;
}
.job-tile-thumb::before { top: 0; }
.job-tile-thumb::after { bottom: 0; }
.job-tile-thumb .job-thumb-fallback { font-size: 2.4rem; }
/* Centered tile text (2026-10-07, per SquirellMaster - "center the videos, center the
   text" matching the same centering pass just done on the clip-browser page). */
.job-tile-link-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.35rem;
  padding: 0.75rem 1rem;
}
/* Standout pill, not quiet text (2026-10-07, per SquirellMaster - this was the main call
   to action on every tile but read as a subtle afterthought). Still just a <span> inside
   the existing full-tile <a> (the whole tile is one click target - see the comment above
   .job-tile-grid), styled to LOOK like a real button so it's obviously where to go, with
   the same gold-gradient 3D language as every actual .btn on the site. Hover is driven by
   the parent .job-tile-link:hover so it still lifts together with the rest of the tile. */
.job-tile-expand-hint {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--gold-text);
  background: linear-gradient(135deg, var(--gold-light), var(--gold));
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  margin-top: 0.3rem;
  width: fit-content;
  box-shadow: 0 2px 0 rgba(120, 66, 10, 0.5), 0 4px 10px rgba(232, 137, 26, 0.3);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.job-tile-link:hover .job-tile-expand-hint {
  transform: translateY(-1px);
  box-shadow: 0 3px 0 rgba(120, 66, 10, 0.5), 0 6px 14px rgba(232, 137, 26, 0.5);
}
.job-tile-controls {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
  padding: 0 1rem 1rem;
}
.job-tile-controls .job-card-actions { justify-content: center; }

/* Admin queue re-run grouping (2026-09-21) - jobs sharing the same client+vod_url collapse
   under their original instead of cluttering the flat list as separate rows. Same ▸/▾
   disclosure-marker convention as .job-group-summary above, sized down to sit inside a
   table row instead of a full card. */
.rerun-group-row td { padding-top: 0; padding-bottom: 0.5rem; border-top: none; }
.rerun-group-row summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--gold-light, #d4a656);
  list-style: none;
}
.rerun-group-row summary::-webkit-details-marker { display: none; }
.rerun-group-row summary::before { content: "▸ "; }
.rerun-group-row details[open] > summary::before { content: "▾ "; }
.rerun-list { margin: 0.4rem 0 0; padding-left: 1.2rem; font-size: 0.85rem; }
.rerun-list li { margin-bottom: 0.3rem; }

/* Rename-a-job (2026-09-18) */
.job-name-wrap {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.job-name-display {
  font-weight: 600;
}
.job-name-edit-btn {
  background: none;
  border: none;
  color: var(--silver-muted, #8a93a6);
  cursor: pointer;
  font-size: 0.85rem;
  padding: 0.1rem 0.3rem;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.1s ease, color 0.1s ease, background 0.1s ease;
}
.job-name-wrap:hover .job-name-edit-btn {
  opacity: 1;
}
.job-name-edit-btn:hover {
  color: var(--gold);
  background: rgba(232, 137, 26, 0.12);
}
.job-name-edit-row {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.job-name-input {
  background: var(--navy-3);
  border: 1px solid var(--gold);
  color: var(--silver);
  border-radius: 6px;
  padding: 0.25rem 0.5rem;
  font-size: 0.9rem;
  width: 14rem;
  max-width: 40vw;
}
.job-name-save-btn, .job-name-cancel-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 0.9rem;
  padding: 0.15rem 0.35rem;
  border-radius: 4px;
}
.job-name-save-btn { color: var(--success, #3ecf6a); }
.job-name-save-btn:hover { background: rgba(62, 207, 106, 0.15); }
.job-name-cancel-btn { color: var(--error, #e24b4b); }
.job-name-cancel-btn:hover { background: rgba(226, 75, 75, 0.15); }

.hubcast-recording-badge {
  background: rgba(232, 137, 26, 0.15);
  color: var(--gold);
  border: 1px solid rgba(232, 137, 26, 0.4);
}

/* Real marquee lights, not just a gold outline (2026-10-07, per SquirellMaster - "not just
   the yellow around the AI-Assisted Clipping thing"). A row of small glowing bulbs along
   the top edge (a different visual than the film-reel sprocket dots used elsewhere - these
   glow, they're not punched holes) plus a slow pulsing border glow so the whole card reads
   as a lit marquee sign, not a static box with a colored line around it. */
.service-spotlight {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  position: relative;
  border-color: var(--gold);
  animation: marquee-spotlight-glow 3s ease-in-out infinite;
}
.service-spotlight .btn { flex-shrink: 0; }
.service-spotlight::before {
  content: "";
  position: absolute; top: -3px; left: 5%; right: 5%; height: 6px;
  background-image: radial-gradient(circle, var(--gold-light) 1.8px, transparent 2.2px);
  background-size: 22px 6px; background-repeat: repeat-x; background-position: 0 center;
  filter: drop-shadow(0 0 3px rgba(247, 185, 85, 0.9));
  pointer-events: none;
}
@keyframes marquee-spotlight-glow {
  0%, 100% { box-shadow: 0 0 10px rgba(232, 137, 26, 0.25), inset 0 0 16px rgba(232, 137, 26, 0.05); }
  50% { box-shadow: 0 0 22px rgba(232, 137, 26, 0.55), inset 0 0 24px rgba(232, 137, 26, 0.12); }
}

/* Dashboard "Latest job" headline card (2026-10-06, dashboard->Home redesign) - same
   flex row/space-between language as .service-spotlight above, just without the gold
   border-color accent since this isn't a sales pitch. */
.latest-job-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.latest-job-card .btn { flex-shrink: 0; }
/* Home-page reorder/polish pass (2026-10-06, per SquirellMaster's live-screenshot review):
   the headline card now groups a real thumbnail + name/status/date on the left
   (.latest-job-main) and its two now-always-distinct actions on the right
   (.latest-job-actions) - Download (.btn, primary) next to View in Your Jobs
   (.btn.secondary), never the same gesture. */
.latest-job-main {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-width: 0;
}
.latest-job-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  flex-shrink: 0;
}
/* Same source-order specificity bug as .job-tile-thumb above - compounded for the same
   reason (this rule also sits before the .job-thumb base rule in the file). */
.job-thumb.latest-job-thumb {
  width: 140px;
  height: 79px;
  flex: 0 0 auto;
}
.latest-job-thumb .job-thumb-fallback { font-size: 2rem; }
.home-welcome-sub { margin: -0.4rem 0 1.5rem; }
/* Token area wrapped back in a real .card for the reorder/polish pass (2026-10-06) - it
   now leads the page, so it gets the same card treatment as every other section instead of
   bare paragraphs sitting directly on the page background. */
.token-summary-card { padding-bottom: 1.1rem; }

.token-buy-form { text-align: right; }
.token-buy-form label.hint { margin-bottom: 0.3rem; font-size: 0.78rem; }
.token-buy-form select { width: auto; display: inline-block; margin-top: 0.2rem; padding: 0.3rem 0.5rem; }
/* Tip-jar nudge as a little badge, not bare underlined text (2026-10-07, per SquirellMaster
   - "something's missing... let's put a little image to it"). Coffee emoji does the job of
   an icon with zero new asset, styled as a small warm pill so it reads as its own inviting
   little call-to-action instead of a plain sentence-ending link. */
.tip-jar-link {
  display: inline-flex; align-items: center; gap: 0.35rem;
  background: rgba(232, 137, 26, 0.1); border: 1px solid rgba(232, 137, 26, 0.35);
  border-radius: 999px; padding: 0.2rem 0.7rem; margin-left: 0.15rem;
  font-weight: 700; font-size: 0.82rem; color: var(--gold-light); text-decoration: none;
  transition: transform 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}
.tip-jar-link:hover {
  background: rgba(232, 137, 26, 0.18); border-color: var(--gold);
  color: var(--gold); transform: translateY(-1px);
}

.token-package-grid { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: flex-end; }
/* Real pop pass on these specific buttons (2026-10-07, per SquirellMaster - "still look
   kind of cheap... a little more pill shape"). They already carried .btn.secondary's gold
   tint, but at the DEFAULT 6px radius + bare 0.4rem padding a 2-line label just read as a
   cramped dark rectangle - a noticeably rounder shape, more breathing room, and a slightly
   stronger tint than the generic secondary default make these read as real, deliberate
   choices instead of leftover plain boxes. */
.token-pkg-btn {
  font-size: 0.78rem; line-height: 1.4; padding: 0.6rem 1.1rem; text-align: center;
  border-radius: 16px; min-width: 92px;
  background: rgba(232, 137, 26, 0.09);
}
.token-pkg-btn strong { display: block; font-size: 0.95rem; margin-top: 0.15rem; color: var(--gold-light); }
.token-pkg-btn:hover strong { color: var(--gold); }
.token-buy-form details { text-align: right; }
.token-buy-form details summary { cursor: pointer; }

.service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 1rem;
}
.service-card {
  background: var(--navy-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.service-card h4 { margin: 0; font-size: 1rem; color: var(--silver); }
.service-card .price { color: var(--gold-light); font-size: 0.82rem; font-weight: 600; }
.service-card .blurb { color: var(--silver-muted); font-size: 0.85rem; line-height: 1.45; flex-grow: 1; }
.service-card .phase2-badge {
  align-self: flex-start;
  background: var(--warning-bg);
  color: var(--warning);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
}
.category-heading {
  margin: 2.25rem 0 1rem;
  color: var(--gold-light);
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.4rem;
}
.category-heading:first-of-type { margin-top: 0.5rem; }

/* "Now Playing" feature reel (2026-10-07, SquirellMaster's "rolling movie reel" ask) -
   replaces the homepage's old static 5-box "What's live right now" grid with a slow,
   continuously scrolling filmstrip, so the live tools read like a marquee reel instead of
   a wall of identical boxes. home.html renders the card list TWICE back to back inside
   .feature-reel-track so the loop point at translateX(-50%) is seamless. The edge mask
   fades the strip to transparent at both ends instead of hard-cutting mid-card. Hovering
   pauses the scroll so someone can actually stop and read one. */
.feature-reel {
  overflow: hidden;
  position: relative;
  margin: 0 -0.25rem;
  padding: 0.6rem 0 0.9rem;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.feature-reel-track {
  display: flex;
  gap: 1.1rem;
  width: max-content;
  animation: feature-reel-scroll 34s linear infinite;
}
.feature-reel:hover .feature-reel-track { animation-play-state: paused; }
@keyframes feature-reel-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.feature-reel-card {
  flex: 0 0 auto;
  width: 250px;
  background: linear-gradient(160deg, var(--navy-3), var(--navy-2));
  border: 1px solid var(--gold);
  border-radius: 10px;
  padding: 1.4rem 1.1rem 1.1rem;
  position: relative;
  box-shadow: 0 0 0 1px rgba(232, 137, 26, 0.15), 0 8px 24px rgba(0, 0, 0, 0.4);
}
.feature-reel-card::before, .feature-reel-card::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 11px;
  background-image: radial-gradient(circle, var(--navy) 2.6px, transparent 2.7px);
  background-size: 20px 11px;
  background-repeat: repeat-x;
  background-position: 10px center;
}
.feature-reel-card::before { top: 0; }
.feature-reel-card::after { bottom: 0; }
.feature-reel-card h4 { margin: 0.2rem 0 0.5rem; color: var(--gold-light); font-size: 1rem; }
.feature-reel-card .blurb { color: var(--silver-muted); font-size: 0.85rem; line-height: 1.45; margin: 0; }
@media (max-width: 700px) {
  .feature-reel-track { animation-duration: 22s; }
  .feature-reel-card { width: 210px; }
}

/* Header token-balance/coupon merge (dashboard.html, 2026-10-06) - replaces the old
   standalone .token-balance card + separate coupon card with one inline line under the
   "Hey {{ username }}" h1. */
.token-balance-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin: 0 0 0.3rem;
}
.token-balance-hint { margin: 0 0 1.25rem; }
.coupon-inline { font-size: 0.85rem; }
.coupon-inline summary { cursor: pointer; color: var(--silver-muted); }
.coupon-inline .inline-form { margin-top: 0.4rem; }

/* Job cards with real server-rendered thumbnails (2026-10-11 - replaced the old
   client-side drawImage(video) technique, which went solid-black site-wide with no
   server fallback to recover from). Base .job-thumb/.job-card-main/-meta/-actions rules
   below are shared by two different layouts: dashboard.html's single "Latest job"
   headline card (.latest-job-thumb modifier, horizontal) and your_jobs.html's tile grid
   (.job-tile-thumb modifier, stacked - see the .job-tile-grid rules above for that
   redesign, 2026-10-06). */
.job-thumb {
  position: relative;
  width: 112px;
  height: 63px;
  flex: 0 0 auto;
  border-radius: 6px;
  overflow: hidden;
  background: var(--navy-3);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.job-thumb:hover {
  border-color: var(--gold);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}
.job-thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.2s ease;
}
.job-thumb:hover .job-thumb-img { transform: scale(1.06); }
.job-thumb-fallback { font-size: 1.6rem; opacity: 0.55; }
.job-card-main {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  flex: 1 1 auto;
  min-width: 0;
}
.job-card-meta {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
/* Scoped to the grid-tile context only (job_detail.html's own header reuses this same
   .job-card-meta class but stays left-aligned as a normal page header - see the comment
   on .job-detail-header). */
.job-tile-link-meta .job-card-meta { justify-content: center; }
.job-card-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* --------------------------------------------------------------- forms */
.stack { display: flex; flex-direction: column; }
label { display: block; margin-bottom: 1rem; font-size: 0.9rem; color: #ccd2dc; text-align: left; }

input[type="email"], input[type="text"], input[type="password"], input[type="url"], input[type="file"],
input[type="number"], textarea, select {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.6rem 0.65rem;
  background: var(--navy);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--silver);
  font-family: inherit;
}
textarea { resize: vertical; min-height: 5.5rem; }
input:focus, select:focus, textarea:focus { outline: 1px solid var(--gold); border-color: var(--gold); }

fieldset {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem 1rem 1rem;
  margin-bottom: 1rem;
}
fieldset legend { padding: 0 0.4rem; color: var(--silver-muted); font-size: 0.85rem; }
label.radio, label.checkbox {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-right: 1.25rem;
  margin-bottom: 0.4rem;
  font-size: 0.9rem;
}
label.radio input, label.checkbox input { width: auto; margin: 0; }

/* Platform/Source selectable cards (service_form.html polish pass, 2026-10-07) - the
   site owner called the bare native radios in these two groups "cheap"; this restyles
   them as clickable card/chip buttons, same accent-on-select visual language as
   .hub-editor-rail-btn/.account-rail-btn elsewhere in this app (left-accent-free here
   since these sit in a wrapping row, not a vertical rail - border + tint instead).
   Markup technique: the real <input type="radio"> is visually hidden (NOT display:none -
   that would drop it from the tab order) via .card-radio, and its sibling <label> (must
   be an adjacent sibling, not a wrapping parent, for the :checked + label selector below
   to work) becomes the actual clickable card via .card-radio-label. Functional behavior
   (name="platform"/name="source", required-ness, the existing disable-for-TikTok/
   YouTube JS) is untouched by this - CSS/markup only. */
.card-radio-group { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.card-radio-group legend { margin-bottom: 0.15rem; }
.card-radio {
  position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}
.card-radio-label {
  display: inline-flex; align-items: center; cursor: pointer; user-select: none;
  background: var(--navy-3); border: 1px solid var(--border); border-radius: 8px;
  padding: 0.55rem 1.1rem; font-size: 0.88rem; font-weight: 600; color: var(--silver-muted);
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.card-radio-label:hover { color: var(--silver); border-color: var(--gold-light); }
.card-radio:checked + .card-radio-label {
  background: rgba(232, 137, 26, 0.12); border-color: var(--gold); color: var(--gold-light);
}
.card-radio:focus-visible + .card-radio-label { outline: 2px solid var(--gold); outline-offset: 2px; }
.card-radio:disabled + .card-radio-label {
  opacity: 0.45; cursor: not-allowed; color: var(--silver-muted); border-color: transparent;
  background: transparent;
}

/* "Pick from your videos" grid (same feature/date as the cards above) - thumbnail +
   title + date tiles, same rounded/bordered tile language as .job-tile above but sized
   down for a picker list and scrollable instead of paging. */
.my-videos-state { color: var(--silver-muted); font-size: 0.85rem; margin: 0.5rem 0; }
.my-videos-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 0.65rem; max-height: 420px; overflow-y: auto; margin-top: 0.5rem; padding: 0.15rem;
}
.my-video-tile {
  display: flex; flex-direction: column; gap: 0.3rem; text-align: left; cursor: pointer;
  background: var(--navy-3); border: 1px solid var(--border); border-radius: 8px;
  padding: 0.5rem; font-family: inherit; color: var(--silver);
}
.my-video-tile:hover { border-color: var(--gold-light); }
.my-video-tile.selected { border-color: var(--gold); background: rgba(232, 137, 26, 0.12); }
.my-video-tile img {
  width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 4px;
  background: #000; display: block;
}
.my-video-tile-title {
  font-size: 0.8rem; font-weight: 700; color: var(--silver); overflow: hidden;
  text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.my-video-tile-date { font-size: 0.72rem; color: var(--silver-muted); }

.or { text-align: center; color: var(--silver-muted); margin: 1rem 0; }

/* 3D neon "pop" button treatment (2026-10-07 site-wide redo) - the old flat gradient
   fill + opacity-on-hover read as cheap/blocky across the whole site (every .btn/button
   shares this one rule block, so this single redo covers every button everywhere). Real
   depth comes from a hard drop shadow doubling as a bottom "edge" (so it looks pressed,
   not just colored) plus a soft gold glow outside that; hover lifts it 1px and brightens
   the glow, :active presses it back down - a real physical button feel instead of a
   plain opacity dim. */
button, .btn, .discord-btn {
  background: linear-gradient(135deg, var(--gold-light), var(--gold));
  color: var(--gold-text);
  border: none;
  padding: 0.62rem 1.3rem;
  border-radius: 8px;
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  font-family: inherit;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  box-shadow: 0 2px 0 rgba(120, 66, 10, 0.55), 0 4px 14px rgba(232, 137, 26, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.25);
  transform: translateY(0);
  transition: transform 0.12s ease, box-shadow 0.12s ease, opacity 0.12s ease;
}
button:hover, .btn:hover, .discord-btn:hover {
  opacity: 1;
  transform: translateY(-1px);
  box-shadow: 0 3px 0 rgba(120, 66, 10, 0.55), 0 6px 20px rgba(232, 137, 26, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
button:active, .btn:active, .discord-btn:active {
  transform: translateY(1px);
  box-shadow: 0 1px 0 rgba(120, 66, 10, 0.55), 0 2px 8px rgba(232, 137, 26, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
/* Gold-tinted outline, not flat gray (2026-10-07 - SquirellMaster: "not just such plain
   outlines... let's add some pop"). Every secondary button site-wide was a plain
   var(--border) gray box until it was hovered - now it carries a faint gold tint and
   background wash at rest too, so it reads as part of the theme from the first paint
   instead of looking unfinished until a hover proves it belongs. */
button.secondary, .btn.secondary {
  background: rgba(232, 137, 26, 0.05);
  color: var(--silver);
  border: 1px solid rgba(232, 137, 26, 0.3);
  box-shadow: none;
}
button.secondary:hover, .btn.secondary:hover {
  border-color: var(--gold);
  color: var(--gold-light);
  background: rgba(232, 137, 26, 0.1);
  box-shadow: 0 0 12px rgba(232, 137, 26, 0.35);
  transform: translateY(-1px);
}
/* Real global small-button size (2026-10-07 fix) - .small was previously only wired up
   inside .clip-preview-actions; everywhere else (Your Jobs tiles' Download All / Request
   Re-Clip, tip.html's amount buttons, admin tables, etc.) a `small` class on a button did
   literally nothing, so "small" buttons sitting next to each other rendered at full
   default size and looked mismatched. This is the one rule that was missing. */
.btn.small, button.small, .discord-btn.small {
  font-size: 0.8rem;
  padding: 0.42rem 0.85rem;
}
button.danger, .btn.danger {
  background: var(--error);
  color: #2a0a0a;
  box-shadow: 0 2px 0 rgba(90, 10, 10, 0.5), 0 4px 14px rgba(200, 40, 40, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

/* Tertiary/link-style button (dashboard header polish, 2026-10-06) - no border, no fill,
   for actions that shouldn't compete visually with the real primary/secondary buttons
   (e.g. "Buy Hub Tokens" / "Gift Hub Tokens" demoted to small text-links next to the
   token balance). Intentionally scoped to .btn.link only - Hub Editor's buttons are
   separate work, out of scope here. */
.btn.link {
  background: none;
  border: none;
  padding: 0.1rem 0;
  color: var(--gold-light);
  font-weight: 600;
  text-decoration: none;
  box-shadow: none;
}
.btn.link:hover { opacity: 1; text-decoration: underline; transform: none; box-shadow: none; }
.btn.link.small { font-size: 0.85rem; }

/* Real disabled look (polish pass 2026-10-06) - ported from clip_flip_picker.html's old
   one-off inline disabled styling on its Reset Layout button (#232a3d/#c5cbe0/#3a4257),
   converted to the real CSS vars so it matches the rest of the site's palette instead of
   a literal hex one-off, and made reusable for every `.btn`/`button` (Hub Editor's
   "nothing to do yet" guards use real `disabled` attributes now instead of alert()s - see
   hub_editor.html). Specificity (class + pseudo-class) already beats plain `.btn`,
   `.btn.secondary`, and `button.danger` above without needing !important. */
.btn:disabled {
  background: var(--navy-3);
  color: var(--silver-muted);
  border: 1px solid var(--border);
  cursor: not-allowed;
  opacity: 1;
  box-shadow: none;
  transform: none;
}
.btn:disabled:hover { opacity: 1; box-shadow: none; transform: none; }

/* ----------------------------------------------------------------- misc */
/* Wraps any wide table (e.g. the Clients table, which genuinely needs more
   columns than a card is wide) so it scrolls INSIDE its own card instead of
   spilling its content out past the card's background/border. */
.table-scroll { overflow-x: auto; margin: 0 -1.5rem; padding: 0 1.5rem; }
.table-scroll table { min-width: 100%; }
.table-scroll th, .table-scroll td { white-space: nowrap; }
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 0.6rem 0.7rem; border-bottom: 1px solid var(--border); font-size: 0.88rem; }
th { color: var(--silver-muted); font-weight: 600; text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.04em; border-bottom: 2px solid var(--gold); }
tbody tr:hover { background: rgba(255, 255, 255, 0.03); }

.status { padding: 0.15rem 0.55rem; border-radius: 4px; font-size: 0.78rem; font-weight: 700; }
.status-queued { background: var(--warning-bg); color: var(--warning); }
.status-processing { background: var(--warning-bg); color: var(--warning); }
.status-done { background: var(--success-bg); color: var(--success); }
.status-failed { background: var(--error-bg); color: var(--error); }
.status-approved { background: var(--success-bg); color: var(--success); }
.status-pending { background: var(--warning-bg); color: var(--warning); }
.status-paid { background: var(--success-bg); color: var(--success); }
.status-invited { background: var(--success-bg); color: var(--success); }
.status-rejected, .status-declined, .status-cancelled { background: var(--error-bg); color: var(--error); }
.status-manual_review { background: var(--warning-bg); color: var(--warning); }
.status-open { background: var(--success-bg); color: var(--success); }
.status-closed { background: rgba(154, 163, 184, 0.15); color: var(--silver-muted); }

.error { color: var(--error); background: var(--error-bg); border-radius: 6px; padding: 0.6rem 0.85rem; margin-bottom: 1rem; }
.success { color: var(--success); background: var(--success-bg); border-radius: 6px; padding: 0.6rem 0.85rem; margin-bottom: 1rem; }

.footer {
  text-align: center;
  margin-top: 3rem;
  padding: 1.5rem;
  font-size: 0.78rem;
  color: #5c6473;
}

.job-detail dl { display: grid; grid-template-columns: 170px 1fr; gap: 0.5rem 1rem; font-size: 0.9rem; }
.job-detail dt { color: var(--silver-muted); }
.job-detail dd { margin: 0; word-break: break-word; }

.inline-form { display: flex; gap: 0.4rem; align-items: center; margin: 0; }
.inline-form input { margin: 0; display: inline-block; width: auto; }

/* ------------------------------------------------------- bot panel (MEE6-style) */
.panel-layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 800px) {
  .panel-layout { grid-template-columns: 1fr; }
}
.panel-nav {
  position: sticky;
  top: 5rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  background: rgba(16, 21, 31, 0.75);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem;
}
.panel-nav button {
  background: transparent;
  border: none;
  color: var(--silver-muted);
  text-align: left;
  padding: 0.55rem 0.7rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
  font-family: inherit;
}
.panel-nav button:hover { background: var(--navy-3); color: var(--silver); }
.panel-nav button.active {
  background: linear-gradient(135deg, var(--gold-light), var(--gold));
  color: var(--gold-text);
}
.panel-section { display: none; }
.panel-section.active { display: block; }
.panel-module-card {
  background: rgba(16, 21, 31, 0.75);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.4rem 1.5rem;
  margin-bottom: 1.25rem;
}
.panel-module-card h3 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.9rem;
  font-size: 1.05rem;
}
.panel-module-card .field { margin-bottom: 1rem; }
.panel-module-card .field:last-child { margin-bottom: 0; }

/* Toggle switch (real switches, not raw checkboxes, per the MEE6-dashboard
   design bar) - a checkbox visually hidden but still the real form control,
   so no JS is needed for the form to submit correctly. */
.switch { position: relative; display: inline-flex; align-items: center; gap: 0.6rem; cursor: pointer; margin-bottom: 1rem; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  width: 42px; height: 24px; border-radius: 999px;
  background: var(--navy); border: 1px solid var(--border);
  position: relative; flex-shrink: 0; transition: background 0.15s ease;
}
.switch .track::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--silver-muted); transition: transform 0.15s ease, background 0.15s ease;
}
.switch input:checked + .track { background: var(--gold); border-color: var(--gold); }
.switch input:checked + .track::after { transform: translateX(18px); background: var(--gold-text); }
.switch .switch-label { font-size: 0.9rem; color: #ccd2dc; }

.help-block {
  background: var(--navy);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.7rem 0.9rem;
  font-size: 0.82rem;
  color: var(--silver-muted);
  margin-bottom: 1rem;
  line-height: 1.5;
}
.help-block code { color: var(--gold-light); }
.placeholder-pill {
  display: inline-block;
  background: var(--navy-3);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.05rem 0.4rem;
  font-family: monospace;
  font-size: 0.78rem;
  color: var(--gold-light);
  margin: 0.1rem 0.15rem 0.1rem 0;
}
.gate-notice {
  text-align: center;
  padding: 2.5rem 1.5rem;
}
.gate-notice .price-tag { color: var(--gold-light); font-weight: 700; }

/* ------------------------------------------------------------ avatar / bio */
.avatar-row { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; }
.avatar-preview {
  width: 84px; height: 84px; border-radius: 50%;
  object-fit: cover; border: 2px solid var(--border);
}
.avatar-row form { margin: 0; }
.avatar-row input[type="file"] { width: auto; }

/* ------------------------------------------------------------ link-in-bio */
/* Hub Card scrub pass (2026-10-07, per SquirellMaster - "same kind of scrub... see what
   we can change/update"). Gold-tinted border + hover lift matching the rest of the site;
   the up/down/remove controls become small circular icon buttons (same depth language as
   the clip-feedback thumbs) instead of plain bare-bordered squares. */
.link-row {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.55rem 0.7rem; border: 1px solid rgba(232, 137, 26, 0.2); border-radius: 8px;
  margin-bottom: 0.5rem; background: var(--navy-3);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.link-row:hover {
  transform: translateY(-1px);
  border-color: rgba(232, 137, 26, 0.45);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
}
.link-row .link-icon-preview { flex-shrink: 0; display: flex; }
.link-row .link-icon-preview .link-icon { width: 1.1rem; height: 1.1rem; }
.link-row .link-info { flex-grow: 1; min-width: 0; }
.link-row .link-label { font-weight: 600; color: var(--silver); }
.link-row .link-url { color: var(--silver-muted); font-size: 0.8rem; word-break: break-all; }
.link-row form { margin: 0; }
.link-row .icon-btn {
  background: var(--navy-2); border: 1px solid var(--border); color: var(--silver-muted);
  width: 1.9rem; height: 1.9rem; padding: 0; border-radius: 50%; cursor: pointer;
  font-size: 0.85rem; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
  transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.link-row .icon-btn:hover { border-color: var(--gold); color: var(--gold-light); transform: translateY(-1px); }
.add-link-form { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: flex-end; margin-top: 0.75rem; }
.add-link-form label { flex: 1 1 180px; margin-bottom: 0; }
.add-link-form button { flex: 0 0 auto; }
/* Share box upgrade - a real "live" pulse dot + gold-tinted frame instead of a plain gray
   box, since this is the one thing on the page meant to be copied/shared. */
.public-url-box {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  background: var(--navy); border: 1px solid rgba(232, 137, 26, 0.3); border-radius: 8px;
  padding: 0.7rem 0.9rem; font-size: 0.85rem; margin-top: 0.5rem;
}
.public-url-live-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--success);
  box-shadow: 0 0 6px var(--success); flex-shrink: 0;
}
.hubtree-section-heading { color: var(--gold-light); }
.hub-tree-display-name-label { display: block; font-weight: 600; margin-bottom: 0.4rem; }
.hubtree-preview-frame-wrap {
  margin-top: 0.75rem; border: 1px solid rgba(232, 137, 26, 0.25); border-radius: 10px;
  overflow: hidden; background: var(--navy); max-width: 420px;
}
.hubtree-preview-frame { width: 100%; height: 560px; border: none; display: block; background: var(--navy); }
.hubtree-color-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.75rem; margin-bottom: 0.75rem;
}
.hubtree-color-grid input[type="color"] {
  width: 100%; height: 2.4rem; padding: 0.2rem; border-radius: 6px;
  border: 1px solid var(--border); background: var(--navy); cursor: pointer;
}
.public-url-box code { color: var(--gold-light); word-break: break-all; }

/* ---------------------------------------------------- public link-in-bio page */
.linkbio { max-width: 420px; margin: 2rem auto; text-align: center; }
.linkbio .avatar-preview { width: 104px; height: 104px; margin-bottom: 0.75rem; }
.linkbio h1 { margin-bottom: 0.15rem; }
.linkbio .bio-text { color: var(--silver-muted); margin-bottom: 1.75rem; white-space: pre-wrap; }
.linkbio .bio-link {
  display: block; width: 100%; padding: 0.85rem 1rem; margin-bottom: 0.85rem;
  background: var(--navy-3); border: 1px solid var(--border); border-radius: 999px;
  color: var(--silver); text-decoration: none; font-weight: 600; text-align: center;
}
.linkbio .bio-link:hover { border-color: var(--gold); color: var(--gold-light); }

.tab-nav { display: flex; gap: 0.5rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
.tab-nav a {
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--silver-muted);
  text-decoration: none;
  font-size: 0.85rem;
}
.tab-nav a.active { border-color: var(--gold); color: var(--gold-light); }

/* --------------------------------------------------------------- testimonials */
.testimonial-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1rem;
}
/* Testimonials redone as "ticket stub" cards (2026-10-07, SquirellMaster's "dress up what
   clients say" ask) - a dashed gold top edge + a big quote-mark watermark behind the text
   is the theater-ticket cue, kept subtle (no fabricated copy - t.quote_text/username are
   real stored client testimonials, this only changes the frame around them). Hover lifts
   the card slightly, matching the same depth language as the new buttons/reel cards. */
.testimonial-card {
  background: linear-gradient(160deg, var(--navy-3), var(--navy-2));
  border: 1px solid var(--border);
  border-top: 3px dashed var(--gold);
  border-radius: var(--radius);
  padding: 1.3rem 1.1rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  position: relative;
  overflow: hidden;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.testimonial-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(232, 137, 26, 0.25);
}
.testimonial-card::before {
  content: "\201C";
  position: absolute;
  top: -1.2rem; right: 0.4rem;
  font-size: 5rem;
  font-family: Georgia, serif;
  color: rgba(232, 137, 26, 0.12);
  line-height: 1;
  pointer-events: none;
}
.testimonial-stars { color: var(--gold-light); letter-spacing: 0.15em; margin: 0; text-shadow: 0 0 6px rgba(232, 137, 26, 0.4); }
.testimonial-quote { color: var(--silver); font-style: italic; line-height: 1.5; margin: 0; flex-grow: 1; position: relative; z-index: 1; }
.testimonial-author { color: var(--silver-muted); font-size: 0.85rem; margin: 0; }

/* ------------------------------------------------------------- request access */
.access-request-spotlight { max-width: 480px; margin: 2rem auto; }

/* --------------------------------------------------------------- mobile safety
   2026-09-16 polish pass: wide data tables (admin/users.html's 9-column client
   table, admin/job_detail.html's invoices table, dashboard.html's jobs table)
   had no overflow handling at all - on a narrow viewport they'd overflow the
   page instead of scrolling. Table markup itself doesn't need to change for
   this - display:block turns the table into a horizontally-scrollable block
   while keeping its internal grid/alignment intact. */
@media (max-width: 700px) {
  table { display: block; overflow-x: auto; white-space: nowrap; }
}

/* ----------------------------------------------------- notification bell */
/* .notif-bell is now just a positioning wrapper - the real clickable box
   (size/border/radius/hover) lives on .notif-bell>button below, added by the
   V6 accessibility patch (2026-11-02) that made the bell a real <button> for
   keyboard/screen-reader support. Keeping box styles on both this div AND
   the button drew two overlapping boxes (a small circle + a bigger square) -
   the "weird square around it" report. */
.notif-bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--silver-muted);
  font-size: 1.05rem;
}
.notif-badge {
  position: absolute;
  top: -0.1rem;
  right: -0.1rem;
  background: var(--gold);
  color: var(--gold-text);
  font-size: 0.62rem;
  font-weight: 800;
  border-radius: 999px;
  min-width: 1.1rem;
  height: 1.1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.25rem;
}
.notif-dropdown {
  position: absolute;
  right: 0;
  top: calc(100% + 0.5rem);
  width: 320px;
  max-height: 420px;
  overflow-y: auto;
  background: var(--navy-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  z-index: 50;
  display: none;
  flex-direction: column;
}
.notif-dropdown.open { display: flex; }
.notif-dropdown-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--border);
  font-weight: 700;
  font-size: 0.85rem;
}
.notif-dropdown-header button { background: none; border: none; color: var(--gold-light); font-size: 0.75rem; cursor: pointer; padding: 0; }
.notif-item {
  display: block;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: var(--silver);
  font-size: 0.85rem;
}
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: rgba(232, 137, 26, 0.06); }
.notif-item.unread { border-left: 3px solid var(--gold); }
.notif-item .notif-time { display: block; color: var(--silver-muted); font-size: 0.72rem; margin-top: 0.15rem; }
.notif-empty { padding: 1rem 0.9rem; color: var(--silver-muted); font-size: 0.85rem; }

/* --------------------------------------------------------------- tickets */
/* Job Updates page rework (2026-10-07, "give it the same treatment as everything else") -
   this page was still plain bare .card blocks while Your Jobs/dashboard/clip-browser all
   got the gold-tinted-border + hover-lift pass. Reuses that exact same depth language
   instead of inventing a new one. */
.job-update-card {
  border-color: rgba(232, 137, 26, 0.25);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.job-update-card:hover {
  transform: translateY(-2px);
  border-color: rgba(232, 137, 26, 0.5);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(232, 137, 26, 0.15);
}
.job-update-main { display: flex; gap: 1.1rem; align-items: flex-start; }
/* Compounded with .job-thumb - same specificity-order lesson as .job-tile-thumb/
   .latest-job-thumb above (that bare-selector bug is exactly why this one is written
   this way from the start instead of risking the same mistake a third time). */
.job-thumb.job-update-thumb {
  width: 160px; flex: 0 0 auto; aspect-ratio: 16 / 9; position: relative; border-radius: 8px;
}
.job-update-thumb::before, .job-update-thumb::after {
  content: "";
  position: absolute; left: 0; right: 0; height: 8px;
  background-image: radial-gradient(circle, var(--navy) 2px, transparent 2.1px);
  background-size: 16px 8px; background-repeat: repeat-x; background-position: 7px center;
  z-index: 1; pointer-events: none;
}
.job-update-thumb::before { top: 0; }
.job-update-thumb::after { bottom: 0; }
/* Contain, not cover (2026-10-07, per SquirellMaster - the frame was cropped tight against
   the box edges on this specific thumbnail size). Scoped to just this page's thumbnail,
   not the shared .job-thumb-img rule - Your Jobs/dashboard's thumbnails already looked
   right with cover at their own sizes, no reason to risk changing those too. */
.job-update-thumb .job-thumb-img { object-fit: contain; }
.job-update-body { flex: 1 1 auto; min-width: 0; }
.job-update-title {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-top: 0;
}
.job-update-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.3rem; }
@media (max-width: 700px) {
  .job-update-main { flex-direction: column; }
  .job-thumb.job-update-thumb { width: 100%; }
}
/* Nested ticket thread gets the same dashed "ticket stub" top edge as the homepage
   testimonial cards - literally a ticket, so the pun carries over on purpose. */
.ticket-card { margin-top: 0.75rem; border-top: 2px dashed rgba(232, 137, 26, 0.4); padding-top: 0.85rem; }
.ticket-card h4 { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.5rem; }
.ticket-messages { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.6rem; max-height: 340px; overflow-y: auto; }
.ticket-message { background: var(--navy-2); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.7rem; }
.ticket-message-staff { border-left: 3px solid var(--gold); }
.ticket-message-client { border-left: 3px solid var(--silver-muted); }
.ticket-message-sender { font-weight: 700; font-size: 0.8rem; }
.ticket-message-time { margin-left: 0.5rem; }
.ticket-message p { margin: 0.25rem 0 0; white-space: pre-wrap; }
.ticket-attachment-thumb { display: block; max-width: 220px; max-height: 140px; border-radius: 6px; margin-top: 0.4rem; border: 1px solid var(--border); }
.new-ticket-toggle { margin-top: 0.75rem; }
.new-ticket-toggle summary { cursor: pointer; color: var(--gold-light); font-size: 0.85rem; }

/* Active bug reporting widget (added 2026-09-23) - present on every logged-in page (see
   base.html), not scoped to any one template. Moved 2026-09-23 from a floating bottom-right
   button to a small icon next to the notification bell, per SquirellMaster - a persistent
   full-page-corner button was too intrusive; matches .notif-bell's own size/shape exactly
   for visual consistency in the nav bar. */
.nav-bug-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: 999px;
  background: none; border: 1px solid transparent; color: var(--gold);
  cursor: pointer; padding: 0;
}
.nav-bug-btn:hover { color: var(--gold-light); border-color: var(--border); background: rgba(232, 137, 26, 0.08); }
.bug-report-modal {
  display: none;
  position: fixed; inset: 0; z-index: 9999;
  background: rgba(6, 9, 15, 0.72);
  align-items: center; justify-content: center; padding: 1rem;
}
/* Real bug found live 2026-09-23 (SquirellMaster: "pops up in your face... can't get away
   from it", "cancel button doesn't work") - this rule used to set `display: flex` directly
   on the bare .bug-report-modal class, which OVERRODE the [hidden] attribute's built-in
   `display: none` (a class selector beats the UA stylesheet's [hidden] rule on specificity),
   so the modal rendered full-screen on every page load and neither the JS-set hidden
   attribute nor the Cancel button's identical `modal.hidden = true` could ever actually hide
   it - same root cause behind both symptoms. Now display defaults to none above, and only
   switches to flex when the hidden attribute is genuinely absent. */
.bug-report-modal:not([hidden]) { display: flex; }
.bug-report-modal-inner {
  background: var(--navy-2); border: 1px solid var(--border); border-radius: 10px;
  padding: 1.25rem; width: 100%; max-width: 420px; max-height: 85vh; overflow-y: auto;
}
.bug-report-modal-inner h3 { margin: 0 0 0.3rem; }
.bug-report-modal-inner textarea {
  width: 100%; box-sizing: border-box; margin-top: 0.5rem; resize: vertical;
  background: var(--navy); color: var(--silver); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.5rem;
}
.bug-report-preview-wrap { margin-top: 0.6rem; }
.bug-report-preview-wrap img {
  max-width: 100%; max-height: 220px; border-radius: 6px; border: 1px solid var(--border);
  display: block;
}
.bug-report-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 0.75rem; }

/* Hub Editor - three-zone layout (left tool panel / center "stage" card merging the raw
   video + crop-box overlay with the live composited preview canvas / a persistent bottom
   Cut filmstrip). Header + token callout redesigned 2026-10-06 (per SquirellMaster's
   explicit ask to make the very top of the page - breadcrumb, title, context line, price
   callout - read as more professional, not just the tools further down): a proper
   breadcrumb-style back link instead of a bare inline <a>, clearer type hierarchy between
   the H1 and the file/job context line, and the price list restyled from mdash-separated
   inline text into tidy pill badges. */
/* Two back-links now (2026-10-07, per SquirellMaster - a way back to the specific job's
   clip browser, not just all the way out to Dashboard) - flex row with a real gap instead
   of relying on default inline whitespace between them. */
.hub-editor-breadcrumb { margin-bottom: 0.6rem; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.hub-editor-back-link { display: inline-flex; align-items: center; gap: 0.35rem; }

.hub-editor-page-header { margin-bottom: 1.1rem; }
.hub-editor-page-header h1 { margin: 0 0 0.25rem; font-size: 1.7rem; }
.hub-editor-page-context {
  margin: 0; font-size: 0.92rem; color: var(--silver-muted); display: flex;
  flex-wrap: wrap; align-items: baseline; gap: 0.4rem;
}
.hub-editor-context-file { color: var(--silver); font-weight: 600; }
.hub-editor-context-sep { color: var(--border); }
.hub-editor-context-job { color: var(--silver-muted); }

.hub-editor-token-callout {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.6rem; background: var(--navy-3); border: 1px solid var(--border);
  border-left: 4px solid var(--gold); padding: 0.75rem 1.1rem; border-radius: 8px;
  margin-bottom: 1.25rem;
}
/* Reworked 2026-10-06 from an mdash-separated inline string into real pill badges - one
   glance tells you which tools are free vs. token-gated, instead of parsing a sentence. */
.hub-editor-price-list { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0; }
.hub-editor-price-pill {
  font-size: 0.8rem; font-weight: 600; color: var(--silver); background: var(--navy);
  border: 1px solid var(--border); border-radius: 999px; padding: 0.3rem 0.75rem;
}
.hub-editor-price-free { color: var(--success); border-color: rgba(74, 222, 128, 0.35); }
.hub-editor-balance-line { margin: 0; color: var(--silver-muted); font-size: 0.85rem; white-space: nowrap; }

/* Shared spacing/layout classes (polish pass 2026-10-06) - replaces one-off inline
   style="..." layout attributes scattered across the Trim/Text/Captions/Audio panels with
   one consistent spacing scale: 0.5rem (tight), 0.75rem (standard), 1.25rem (loose).
   .hub-editor-field-row (the old Trim "add seconds before/after" number-field row) was
   removed here 2026-10-06 along with the fields themselves - dead once the slider rebuild
   replaced them. */
.hub-editor-radio-stack { display: flex; flex-direction: column; gap: 0.5rem; }
.hub-editor-text-list { margin-top: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem; }
.hub-editor-subwrap { margin-top: 0.75rem; }
.hub-editor-mt-sm { margin-top: 0.5rem; }
.hub-editor-mt-0 { margin-top: 0; }
.hub-editor-status-note { display: none; margin-top: 0.75rem; }
/* Post-to-YouTube panel polish (2026-10-07) - this ran as markup-only for a while with no
   matching CSS (the section headings/show-more/audience cards rendered as unstyled plain
   HTML), left that way when other work interrupted it. Filling in now since it's finally
   getting wired up end to end (JS fetch body + button-enable listener). */
.hub-editor-yt-section-heading {
  margin: 1.25rem 0 0.5rem; color: var(--gold-light); font-size: 0.82rem;
  text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700;
}
.hub-editor-yt-section-heading:first-of-type { margin-top: 0.5rem; }
.hub-editor-yt-show-more { margin-top: 0.9rem; }
.hub-editor-yt-show-more summary {
  cursor: pointer; color: var(--gold-light); font-weight: 600; font-size: 0.88rem;
}
.hub-editor-yt-show-more summary:hover { color: var(--gold); }
.hub-editor-yt-show-more-body { margin-top: 0.6rem; display: flex; flex-direction: column; gap: 0.75rem; }
.hub-editor-yt-audience-intro { margin: 0.3rem 0 0.6rem; }
.hub-editor-yt-audience-group { gap: 0.75rem; }
.hub-editor-yt-audience-card {
  flex-direction: column; align-items: flex-start; text-align: left;
  gap: 0.25rem; width: 220px; padding: 0.75rem 1rem;
}
.hub-editor-yt-audience-title { font-weight: 700; color: inherit; }
.hub-editor-yt-audience-card .subtle { font-size: 0.76rem; line-height: 1.35; font-weight: 400; }
@media (max-width: 700px) {
  .hub-editor-yt-audience-card { width: 100%; }
}
/* renderTextList()'s per-row card - a navy card treatment, same family as the filmstrip
   piece/price-pill styling elsewhere on this page instead of a one-off inline style. */
.hub-editor-text-row {
  background: var(--navy-3); border: 1px solid var(--border); border-radius: 8px; padding: 0.75rem;
}

.hub-editor-inline-check { display: flex; align-items: center; gap: 0.5rem; font-weight: normal; }

/* Trim / Extend panel - added 2026-10-06, ported from the standalone /trim tool's two-
   draggable-handle timeline, then rebuilt the same day into ONE continuous slider spanning
   the full (origStart - extendMax)..(origEnd + extendMax) range (see hub_editor.html's
   trim script for why) instead of two separate UIs (in-bounds drag-handles PLUS "add
   seconds" number fields). The warning callout reuses service_form.html's gold
   border-left-accent pattern (its own "pick your exact game" notice) rather than a new
   look, since applying a trim here resets the other panels' client-side state. */
.hub-editor-trim-notice {
  background: var(--navy-3); border-left: 4px solid var(--gold); border-radius: 6px;
  padding: 0.7rem 1rem; margin-bottom: 0.9rem; color: var(--gold-light); font-weight: 600;
  font-size: 0.85rem;
}
.hub-editor-trim-timeline {
  position: relative; height: 32px; margin: 1rem 0; background: var(--navy-3);
  border-radius: 4px; overflow: hidden;
}
/* Shaded box showing where the clip's ORIGINAL (pre-extend) bounds sit within the full
   slider range - the visual "here's how far you can extend" reference the old two-field
   UI had no equivalent of. */
.hub-editor-trim-orig-range {
  position: absolute; top: 0; bottom: 0; background: rgba(232, 137, 26, 0.12);
  border-left: 1px dashed var(--border); border-right: 1px dashed var(--border);
}
.hub-editor-trim-fill { position: absolute; top: 0; bottom: 0; background: var(--gold); opacity: 0.35; }
.hub-editor-trim-handle {
  position: absolute; top: 0; bottom: 0; width: 10px; background: var(--gold);
  cursor: ew-resize; touch-action: none; z-index: 2; border-radius: 3px;
}

/* Left NAV rail + center stage, side by side on a normal desktop width, stacked below
   that (960px matches the panel-layout/800px convention already used for the bot panel,
   widened a bit since the stage needs real room to breathe). Restructured 2026-10-06, per
   SquirellMaster: this column now holds ONLY the slim tab-button rail (no panel content
   expands here anymore - see .hub-editor-controls below), so it shrank from 300px to 260px
   to match account.html's account-rail width (the explicit "menu style" model for this
   rail) - judgment call, flagged for review: checked visually that "Vertical Reframe"/
   "Trim / Extend" labels still sit on one line at this width without wrapping. */
.hub-editor-layout {
  display: grid; grid-template-columns: 260px 1fr; gap: 1.25rem;
  align-items: start; margin-bottom: 1.25rem;
}
@media (max-width: 960px) {
  .hub-editor-layout { grid-template-columns: 1fr; }
}

.hub-editor-tools { padding: 1.1rem; }

/* NEW wide Controls card (2026-10-06) - sits full-width BELOW the nav+Stage row above,
   holding whichever tab's .hub-editor-panel is currently active (relocated out of the old
   300px rail column so these controls have real room - see hub_editor.html for the panels
   themselves and the tab-click handler that toggles `hidden` on them). */
.hub-editor-controls { padding: 1.25rem; margin-bottom: 1.25rem; }
.hub-editor-controls-empty { margin: 0; text-align: center; color: var(--silver-muted); padding: 0.5rem 0; }
.hub-editor-panel h4 {
  margin: 0 0 0.6rem; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--silver-muted);
}
.hub-editor-panel > p:first-child { margin-top: 0; }
.hub-editor-panel-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* Shared card-title treatment (polish pass 2026-10-06) - Stage used to be a bare <h3>,
   Timeline a bare <strong> inside its header row, and Actions had no title at all. All
   three now share this one look instead of three different ad-hoc treatments. */
.hub-editor-card-title { font-size: 1rem; margin: 0 0 0.9rem; color: var(--gold-light); font-weight: 700; }

/* Left tool rail - rebuilt 2026-10-06 (per SquirellMaster: "make that menu a little bit
   more fluid... line them up more vertically") from a wrapped row of pill buttons (which
   wrapped awkwardly in this column's real ~300px width) into a clean vertical list rail,
   the same basic shape CapCut/Premiere/Opus Clip use for a left-side tool rail: icon +
   label per row, a clear active state (left accent bar + tinted background instead of a
   pill), and groups separated by generous spacing + a small uppercase micro-label instead
   of a hard divider rule. */
.hub-editor-rail {
  display: flex; flex-direction: column; gap: 1.1rem; margin: 0 0 1.25rem;
  padding-bottom: 1rem; border-bottom: 1px solid var(--border);
}
.hub-editor-rail-group { display: flex; flex-direction: column; gap: 0.2rem; }
.hub-editor-rail-group-label {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--silver-muted); margin: 0 0 0.3rem 0.1rem;
}
/* Real <button> elements pick up the site-wide 3D-pop button rule (gradient + glow
   box-shadow) by element-type selector even though this class never asked for it - cascade
   applies per-property, so .hub-editor-rail-btn's own rules winning on background/border
   doesn't stop that bare `button` rule's box-shadow/transform from still landing here too.
   2026-10-07, per SquirellMaster: "remove the glow... make it more like the left side menu"
   - explicitly zeroed out below and rebuilt to match .site-sidebar-link's exact hover/
   active language (flat at rest, icon+text glow on hover/active, no box-shadow ever). */
.hub-editor-rail-btn {
  display: flex; align-items: center; gap: 0.65rem; width: 100%; text-align: left;
  background: transparent; border: 1px solid transparent; border-left: 3px solid transparent;
  color: var(--silver-muted); padding: 0.55rem 0.7rem; border-radius: 7px;
  font-size: 0.86rem; font-weight: 600; cursor: pointer; font-family: inherit;
  box-shadow: none; transform: none;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.hub-editor-rail-icon {
  font-size: 1.05rem; line-height: 1; flex-shrink: 0; width: 1.3em; text-align: center;
  transition: transform 0.18s ease, filter 0.18s ease;
}
.hub-editor-rail-text { flex: 1; transition: color 0.12s ease; }
.hub-editor-rail-btn:hover {
  color: var(--silver); background: var(--navy-3); box-shadow: none; transform: none;
}
.hub-editor-rail-btn:hover .hub-editor-rail-icon { transform: translateY(-1px) scale(1.08); filter: drop-shadow(0 0 5px rgba(232, 137, 26, 0.7)); }
.hub-editor-rail-btn:active { box-shadow: none; transform: none; }
.hub-editor-rail-btn.active {
  background: rgba(232, 137, 26, 0.12); border-left-color: var(--gold);
  color: var(--gold-light); box-shadow: none; transform: none;
}
.hub-editor-rail-btn.active .hub-editor-rail-text {
  animation: sidebar-link-glow-pulse 2.6s ease-in-out infinite;
}
.hub-editor-rail-btn.active .hub-editor-rail-icon {
  animation: sidebar-icon-glow-pulse 2.6s ease-in-out infinite;
}
/* Crop goes soft-disabled once a clip's already been vertically reframed (see
   hub_editor.html's loadedmetadata aspect-ratio check) - same "swap the panel content for
   an explanation" pattern Trim/Extend already uses when clip.start_sec is None, so the tab
   itself stays clickable but its panel has nothing live to show. */
.hub-editor-rail-btn:disabled {
  color: var(--silver-muted); cursor: not-allowed; opacity: 0.45; border-left-color: transparent;
}

/* Account page (templates/account.html) - same left-nav-rail + right-panel-swap mechanism
   as Hub Editor's rail above (grouped buttons, one active state at a time, hidden-attribute
   panel toggle), reused under its own "account-*" prefix since this is a different page.
   Simpler than Hub Editor's: a real two-column layout (rail only on the left, the swapped
   panel content filling its own card on the right) rather than rail+panels sharing one
   column alongside a separate always-visible stage. */
.account-layout {
  display: grid; grid-template-columns: 260px 1fr; gap: 1.25rem;
  align-items: start; margin-bottom: 1.25rem;
}
@media (max-width: 960px) {
  .account-layout { grid-template-columns: 1fr; }
}
.account-rail-card { padding: 1.1rem; }
.account-panels { padding: 1.25rem; }
.account-panel h4 {
  margin: 0 0 0.6rem; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--silver-muted);
}
.account-panel-subhead { margin-top: 1.75rem; }

/* Platform connect rows (Twitch/YouTube on Connected Accounts, added 2026-10-07) - same
   visual rhythm as the existing Discord card above them in account.html, just with a real
   brand logo (reused from _social_icons.html's social_icon macro, mode='brand') instead of
   plain text, and a status pill instead of a paragraph. */
/* Pop pass (2026-10-07, per SquirellMaster - "clean up Connected Accounts, make that pop
   more") - gold-tinted border + hover lift matching the rest of the site's card language,
   plus a real visual "this one's live" cue: a connected platform gets a solid gold left
   accent (same visual grammar as an active sidebar link), not just gold text buried in the
   status line. */
.platform-connect-row {
  display: flex; align-items: center; gap: 0.85rem; padding: 0.9rem 1rem;
  border: 1px solid rgba(232, 137, 26, 0.25); border-left: 3px solid transparent;
  border-radius: 8px; background: var(--navy-3);
  margin-top: 0.75rem;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.platform-connect-row:hover {
  transform: translateY(-2px);
  border-color: rgba(232, 137, 26, 0.5);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(232, 137, 26, 0.15);
}
.platform-connect-row:has(.platform-connect-status.connected) { border-left-color: var(--gold); }
.platform-connect-icon {
  flex-shrink: 0; width: 2.4rem; height: 2.4rem; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--navy-2); border: 1px solid var(--border);
}
.platform-connect-icon .link-icon { width: 2rem; height: 2rem; }
.platform-connect-info { flex: 1; min-width: 0; }
.platform-connect-name { font-weight: 700; color: var(--silver); }
.platform-connect-status { font-size: 0.82rem; color: var(--silver-muted); margin-top: 0.1rem; }
.platform-connect-status.connected { color: var(--gold-light); }
.platform-connect-actions { flex-shrink: 0; display: flex; gap: 0.5rem; }
.platform-connect-actions form { margin: 0; }
.platform-connect-row.disabled { opacity: 0.6; }
.platform-connect-row.disabled .platform-connect-icon .link-icon { filter: grayscale(1); }
/* Twitch "!clip" chat bot opt-in (2026-10-07) - a small subrow under the Twitch
   platform-connect-row, same color tokens as the row above rather than new ones. */
.platform-connect-subrow { margin: 0.3rem 0 0.9rem 2.6rem; }
.platform-connect-toggle { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--silver-muted); cursor: pointer; }
.platform-connect-toggle input[type="checkbox"] { width: 1.1rem; height: 1.1rem; cursor: pointer; }

.account-rail { display: flex; flex-direction: column; gap: 1.1rem; }
.account-rail-group { display: flex; flex-direction: column; gap: 0.2rem; }
.account-rail-group-label {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--silver-muted); margin: 0 0 0.3rem 0.1rem;
}
/* Same bare-<button> glow leak + flat-rail fix as .hub-editor-rail-btn above (2026-10-07,
   per SquirellMaster - same "dress that menu back up" ask, same root cause). */
.account-rail-btn {
  display: flex; align-items: center; gap: 0.65rem; width: 100%; text-align: left;
  background: transparent; border: 1px solid transparent; border-left: 3px solid transparent;
  color: var(--silver-muted); padding: 0.55rem 0.7rem; border-radius: 7px;
  font-size: 0.86rem; font-weight: 600; cursor: pointer; font-family: inherit;
  box-shadow: none; transform: none;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.account-rail-icon {
  font-size: 1.05rem; line-height: 1; flex-shrink: 0; width: 1.3em; text-align: center;
  transition: transform 0.18s ease, filter 0.18s ease;
}
.account-rail-text { flex: 1; transition: color 0.12s ease; }
.account-rail-btn:hover { color: var(--silver); background: var(--navy-3); box-shadow: none; transform: none; }
.account-rail-btn:hover .account-rail-icon { transform: translateY(-1px) scale(1.08); filter: drop-shadow(0 0 5px rgba(232, 137, 26, 0.7)); }
.account-rail-btn:active { box-shadow: none; transform: none; }
.account-rail-btn.active {
  background: rgba(232, 137, 26, 0.12); border-left-color: var(--gold);
  color: var(--gold-light); box-shadow: none; transform: none;
}
.account-rail-btn.active .account-rail-text { animation: sidebar-link-glow-pulse 2.6s ease-in-out infinite; }
.account-rail-btn.active .account-rail-icon { animation: sidebar-icon-glow-pulse 2.6s ease-in-out infinite; }

/* Center stage - the live composited preview canvas IS the whole stage now (the raw
   <video> + DOM crop-box overlay that used to sit beside it moved down into the Timeline
   card; crop dragging is now drawn/hit-tested directly on this canvas in JS instead of a
   positioned <div>, so there's no more DOM crop-box CSS here - see drawCropEditingFrame()
   in hub_editor.html for how the box itself gets drawn each frame). */
.hub-editor-stage { padding: 1.25rem; }
/* Real bug fix (2026-10-07, caught live by a real client using Vertical Reframe):
   #hubEditorPreviewCanvas had only `width:100%` and no height cap at all - fine for a
   horizontal clip (16:9 at full card width is a modest height), but a vertical 9:16 clip
   scaled to the SAME full card width comes out roughly DOUBLE that height - the canvas blew
   way past the Stage card and the page, "not fitting" exactly as reported. Capped at 70vh
   (same cap .clip-player-video already uses elsewhere) and centered, so it scales down to
   fit like an image/video would instead of forcing 100% width regardless of aspect ratio. */
.hub-editor-stage-canvas-wrap {
  position: relative; background: #000; border-radius: 6px; overflow: hidden;
  display: flex; justify-content: center;
}
/* Stage transport (2026-10-06) - the Stage is the one true player now, so it gets its own
   minimal play/pause control + time readout instead of relying on a separate scrubber
   video elsewhere on the page. Bottom-left overlay, same "controls float on the player"
   convention as every mainstream video UI. */
.hub-editor-stage-playbtn {
  position: absolute; left: 0.75rem; bottom: 0.75rem; width: 2.4rem; height: 2.4rem;
  border-radius: 50%; background: rgba(10, 14, 22, 0.78); border: 1px solid rgba(255,255,255,0.25);
  color: #fff; font-size: 0.95rem; display: flex; align-items: center; justify-content: center;
  cursor: pointer; z-index: 2;
}
.hub-editor-stage-playbtn:hover { background: rgba(232, 137, 26, 0.85); }
.hub-editor-stage-time {
  position: absolute; right: 0.75rem; bottom: 0.9rem; background: rgba(10, 14, 22, 0.78);
  color: var(--silver); font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.6rem;
  border-radius: 999px; z-index: 2; letter-spacing: 0.02em;
}

/* Vertical Reframe's two-pane Stage mode (2026-10-06) - swaps in for the single composited
   canvas above while the Vertical Reframe tab is active (see hub_editor.html's tab click
   handler). Left pane ports clip_flip_picker.html's draggable-box-over-the-raw-video
   interaction; right pane ports its live stacked/composited layoutBar/layoutCanvas preview,
   both restyled onto this page's real CSS vars instead of clip_flip_picker's inline hex
   one-offs. Judgment call: pane proportions below (flexible left pane, fixed 200px right
   pane) are a starting point for review, not a locked design decision. */
.hub-editor-reframe-stage { display: flex; gap: 1.25rem; flex-wrap: wrap; align-items: flex-start; }
.hub-editor-reframe-stage[hidden] { display: none; }
.hub-editor-reframe-pane { flex: 1 1 280px; min-width: 220px; }
.hub-editor-reframe-pane.hub-editor-reframe-preview-pane { flex: 0 0 auto; width: 200px; }
.hub-editor-reframe-pane-label {
  margin: 0 0 0.5rem; font-size: 0.78rem; color: var(--silver-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.hub-editor-reframe-source {
  position: relative; background: #000; line-height: 0; border-radius: 6px; overflow: hidden;
}
.hub-editor-reframe-source video { width: 100%; display: block; }
.hub-editor-reframe-layout-bar {
  position: relative; width: 100%; aspect-ratio: 9 / 16; background: #000;
  border-radius: 6px; outline: 1px solid var(--border); overflow: hidden;
}
.hub-editor-reframe-layout-bar canvas { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }
.hub-editor-reframe-box-controls { border-top: 1px solid var(--border); margin-top: 0.75rem; }
.hub-editor-reframe-box-row {
  display: flex; align-items: center; gap: 0.7rem; padding: 0.55rem 0;
  border-bottom: 1px solid var(--border);
}
.hub-editor-reframe-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
.hub-editor-reframe-label { min-width: 70px; font-weight: 600; font-size: 0.85rem; }
.hub-editor-reframe-pct { min-width: 48px; text-align: right; font-size: 0.78rem; color: var(--silver-muted); }

/* Bottom Cut card - rebuilt 2026-10-06 (per SquirellMaster, modeled on a CapCut
   screenshot) into one real filmstrip replacing the old separate native-controls <video>
   scrubber + a row of plain colored "segment card" divs entirely. Kept globally persistent
   (visible no matter which tab is active) since the filmstrip is also what drives the
   Stage's live preview/playhead regardless of tab. */
.hub-editor-timeline { padding: 1rem 1.25rem; }
/* The raw <video> every tab's JS still reads/seeks (hubEditorVideo.*) - now a hidden
   decode-only source, not a visible scrubber (the filmstrip below IS the scrubber now).
   Sized near-zero rather than display:none: some browsers throttle/stop decoding a
   display:none video, which would silently break the Stage's live draw AND filmstrip
   thumbnail generation, both of which read frames off this element every tick. */
.hub-editor-decode-video {
  position: absolute; width: 2px; height: 2px; opacity: 0.01; pointer-events: none;
  overflow: hidden;
}
.hub-editor-timeline-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.hub-editor-timeline-header {
  display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap;
  gap: 0.5rem; margin-bottom: 0.75rem;
}
.hub-editor-timeline-title { margin-bottom: 0; }

.hub-editor-filmstrip-wrap { position: relative; }
/* Thin playhead-only scrub bar, its own dedicated surface so dragging it never fights the
   piece row's body-drag-to-reorder / edge-drag-to-trim gestures directly below it. */
.hub-editor-filmstrip-ruler {
  position: relative; height: 14px; background: var(--navy-3); border-radius: 4px 4px 0 0;
  cursor: pointer; margin-bottom: 2px;
}
.hub-editor-filmstrip-playhead {
  position: absolute; top: 0; bottom: -72px; width: 2px; background: var(--gold-light);
  box-shadow: 0 0 4px rgba(232, 137, 26, 0.8); pointer-events: none; z-index: 3; display: none;
}
.hub-editor-filmstrip-playhead::before {
  content: ''; position: absolute; top: 0; left: -4px; width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 7px solid var(--gold-light);
}
.hub-editor-filmstrip-track {
  position: relative; height: 72px; border-radius: 0 0 4px 4px; overflow: hidden;
  cursor: pointer; user-select: none;
}
.hub-editor-filmstrip-canvas { position: absolute; left: 0; top: 0; display: block; width: 100%; height: 100%; }
.hub-editor-filmstrip-overlays { position: absolute; left: 0; top: 0; right: 0; bottom: 0; pointer-events: none; }
.hub-editor-filmstrip-piece {
  position: absolute; top: 0; height: 100%; pointer-events: none;
}
.hub-editor-filmstrip-remove {
  position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; line-height: 1;
  padding: 0; font-size: 0.75rem; border-radius: 50%; pointer-events: auto;
}

/* Restructured 2026-10-06 to carry the same .hub-editor-card-title as Stage/Timeline, and
   to give Render real visual priority over Reset (pushed right via margin-left:auto)
   instead of two equal-weight buttons sitting side by side. */
.hub-editor-actions { padding: 1.1rem 1.25rem; }
.hub-editor-actions-row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.hub-editor-actions-row form { display: flex; gap: 0.6rem; margin: 0; align-items: center; }
.hub-editor-actions-row .subtle { flex: 1 1 260px; margin: 0; }
.hub-editor-reset-btn { margin-left: auto; }

/* Site-wide "updates in progress" banner (2026-10-30, temporary - remove once the
   front-end overhaul ships and this stops being true). Plain top-of-page strip, not
   tied to .card/.topbar styling since it needs to sit visually ABOVE the topbar on
   every page, logged in or not. */
.site-update-banner {
  background: linear-gradient(135deg, var(--gold-light), var(--gold));
  color: var(--gold-text);
  text-align: center;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.55rem 1rem;
  line-height: 1.4;
}



/* Stage 4: stable content, quieter surfaces, visible keyboard focus. */
.site-sidebar-link,.site-sidebar-icon,.service-spotlight,.hero-open-badge,.clip-thumb.active {
  animation:none; text-shadow:none;
}
.site-sidebar-link.active .site-sidebar-text,.site-sidebar-link.active .site-sidebar-icon,
.site-sidebar-link:hover .site-sidebar-text,.site-sidebar-link:hover .site-sidebar-icon,.badge-pill {animation:none;text-shadow:none;filter:none;}
.card,.feature-reel-card,.service-spotlight,.clip-player-frame,.clip-thumb {
  box-shadow:none; border-color:rgba(255,255,255,.16);
}
.site-sidebar-link.active,.clip-thumb.active {border-color:var(--gold);box-shadow:none;}
.btn.secondary,.token-pkg-btn,.job-name-edit-btn {box-shadow:none;background:var(--navy-2);}
.btn.secondary:hover,.token-pkg-btn:hover {box-shadow:none;transform:none;}
.feature-reel {overflow:visible;mask-image:none;-webkit-mask-image:none;}
.feature-reel-track {display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,230px),1fr));width:100%;animation:none;transform:none;gap:1rem;}
.feature-reel-card {width:auto;min-width:0;}
.feature-reel-card::before,.feature-reel-card::after {display:none;}
:focus-visible {outline:3px solid var(--gold-light);outline-offset:3px;}
@media(prefers-reduced-motion:reduce) {
  *,*::before,*::after {animation:none!important;transition:none!important;scroll-behavior:auto!important;}
}




.notif-bell>button {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: inherit; border: 1px solid transparent;
  border-radius: 999px; width: 2rem; height: 2rem; padding: 0; cursor: pointer;
}
.notif-bell>button:hover { color: var(--gold-light); border-color: var(--border); background: rgba(232, 137, 26, 0.08); }
.notif-dropdown[hidden] {display:none!important;}
.site-sidebar {flex-basis:200px;width:200px;}
.site-sidebar-text {display:inline;}
@media(max-width:900px) {
  .app-shell {display:block;}
  .site-sidebar {position:static;width:100%;max-height:none;border-right:0;border-bottom:1px solid var(--border);}
  .site-sidebar-group {display:flex;flex-wrap:wrap;gap:.25rem;}
  .site-sidebar-link {min-height:44px;justify-content:flex-start;padding:.5rem .65rem;}
  .site-sidebar-text,.site-sidebar-group-label,.sidebar-soon-badge {display:inline;}
}




.job-name-edit-btn {opacity:1;min-width:32px;min-height:32px;}
.job-name-wrap:focus-within .job-name-edit-btn {opacity:1;}
@media(pointer:coarse){.job-name-edit-btn{min-width:44px;min-height:44px;}}

