/* ==========================================================================
   VYBE STUDIO UI FRAMEWORK
   17 Sep 2026, Alex: "framework still not matching, we need a firm template
   and CSS and also a product switch from poster > social."

   The shared shell every Vybe Studio product wears: the brand tokens from
   the approved board, the top bar (wordmark, divider, product lockup with
   the product switcher, actions, account), and the small pieces that sit in
   it. Products keep their own page styles; this file owns the frame, so
   Poster, Social and Trailer Vybe look like one app.

   ONE source file, at brand/vybe-ui.css, and EVERY site serves its OWN copy
   from its own origin: Social Vybe's deploy-site.sh stages it into the
   site's public folder, Poster Vybe stages it in netlify-build.sh. Never
   link it across sites (Poster Vybe's agreed shape, 17 Sep 2026). Poster
   Vybe sits behind Netlify password protection, so a cross-site stylesheet
   request would fetch the password page and the header would lose its frame.

   Per product, set --vs-product to that product's accent on its own page.
   ========================================================================== */

:root {
  /* THE BRAND VALUES COME FROM tokens.css (19 Sep 2026, Alex: "wire tokens.css
     in as the single source"). That file is copied VERBATIM out of the brand
     system package, 04-Brand-Tokens/tokens.css, and is never hand edited: when
     the brand changes, re-run scripts/brand/sync-tokens.sh and every site
     follows. The names below are the frame's own, mapped onto the brand's, so
     the rest of this file and every product page keep reading --vs-*.

     Each carries the value as a FALLBACK. If a page ever forgets to load
     tokens.css the frame still paints correctly rather than losing every
     colour to an invalid var(), which is what an unresolved custom property
     would do. The fallback is a safety net, not a second source: it is
     tokens.css that sync-tokens.sh checks. */
  --vs-midnight: var(--vybe-midnight,      #0B0B14);
  --vs-purple:   var(--vybe-vybe-purple,   #8B5CF6);
  --vs-blue:     var(--vybe-aurora-blue,   #3B82F6);
  --vs-pink:     var(--vybe-neon-pink,     #EC4899);
  --vs-orange:   var(--vybe-energy-orange, #F97316);
  --vs-teal:     var(--vybe-vybe-teal,     #14B8A6);
  --vs-mist:     var(--vybe-mist,          #E5E7EB);
  /* the three product roles, named by the brand rather than by colour:
     "Blue belongs to Trailer; pink to Poster; teal to Social." */
  --vs-poster:   var(--vybe-poster,  #EC4899);
  --vs-trailer:  var(--vybe-trailer, #3B82F6);
  --vs-social:   var(--vybe-social,  #14B8A6);
  /* per product: the accent its lockup and primary actions use */
  --vs-product:  var(--vs-social);
  /* THE SLOT CONTRACT (17 Sep 2026, Alex: "it's a multi product solution and
     needs to feel user friendly and comparable"). Every product's bar is the
     same height and carries the same controls in the same order, left to
     right: wordmark, divider, product lockup with the switcher, an optional
     tag, a flexible gap, quick search, the scope toggle, then the right-hand
     cluster of actions and the account avatar. The sizes below are the
     contract; they were taken from Poster Vybe's bar, which was the fuller
     of the two, so muscle memory carries across. */
  /* INPUT: what the bar is, at desktop width. Nothing writes to this. */
  --vs-bar-base-h: 76px;
  /* OUTPUT: what the bar measured, published by each surface's
     ResizeObserver for the page below to offset by. Nothing sizes the bar
     from this, or it latches -- see the note on .vs-bar. */
  --vs-bar-h:    76px;
  --vs-bar-pad:  28px;   /* side padding */
  --vs-bar-gap:  24px;   /* between slots */
  --vs-logo-h:   34px;   /* the wordmark, 26px under 720px */
  --vs-control-h: 38px;  /* search, segments, pills: one height */
  /* the brand names the faces; the frame keeps the platform fallbacks after
     them, which tokens.css does not carry */
  --vs-font-head: var(--vybe-font-heading, "Space Grotesk"), "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  --vs-font-body: var(--vybe-font-body, "Inter"), -apple-system, BlinkMacSystemFont, sans-serif;
}

/* ---- the bar ------------------------------------------------------------ */
/* THE BAR'S HEIGHT IS AN INPUT, --vs-bar-h IS AN OUTPUT (19 Sep 2026).
   These used to be the same token and it latched. Each surface runs a
   ResizeObserver that publishes the bar's measured height into --vs-bar-h so
   the page below can offset by it; while the bar ALSO took its height FROM
   --vs-bar-h, any single bad measurement became permanent. The launcher
   measured 197px once, before the stylesheet had settled, wrote it back, and
   then sat at 197px forever -- a 133px band of empty dark above the hero that
   looked like a Safari bug and was not: Chromium did it too.
   So the bar sizes from --vs-bar-base-h, which nothing writes to, and
   publishes into --vs-bar-h, which nothing reads back. Under 860px the media
   query below sets height:auto so the bar can wrap, and the measurement is
   then the real one. */
.vs-bar {
  position: fixed; inset: 0 0 auto 0; height: var(--vs-bar-base-h, 76px); z-index: 50;
  display: flex; align-items: center; gap: var(--vs-bar-gap); padding: 0 var(--vs-bar-pad);
  background: var(--topbar-bg, rgba(11, 11, 20, .82));
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line, rgba(255, 255, 255, .08));
}
.vs-bar .vs-grow { flex: 1 }
/* the brand cluster keeps its own tighter spacing inside the bar's gap */
.vs-bar .vs-logo + .vs-div, .vs-bar .vs-div + .vs-product { margin-left: calc(var(--vs-bar-gap) * -1 + 12px) }
.vs-logo { display: flex; align-items: center }
.vs-logo img { height: var(--vs-logo-h); display: block }
/* flex: 0 0 1px, not just width, or a busy bar shrinks a 1px item to
   nothing: Poster showed no divider at all while Social and Trailer did. */
.vs-div { flex: 0 0 1px; width: 1px; height: 24px; background: var(--line, rgba(255, 255, 255, .12)) }

/* the product lockup: mark, name in two weights, and the switcher caret */
.vs-product { position: relative }
/* min-height is the contract's control height on purpose (19 Sep 2026, Alex:
   "sort the vertical alignment too"). A wrapped bar centres each row on its
   TALLEST item, so the lockup's vertical position was being decided by
   whatever else that product happened to carry: Trailer's bar holds only the
   lockup, so its row was 32px and the mark sat at y=14, while Social's pills
   made 38 and y=17. Giving the lockup the same height every other control has
   puts a floor under the row, so the mark lands in the same place whatever
   sits beside it. At desktop the bar is a fixed height and everything centres,
   so this changes nothing there. */
.vs-product > button {
  display: flex; align-items: center; gap: 9px; background: none; border: 0; cursor: pointer;
  min-height: var(--vs-control-h);
  color: var(--ink, #fff); font-family: var(--vs-font-head); font-size: 17px; padding: 6px 8px; border-radius: 10px;
}
.vs-product > button:hover { background: rgba(255, 255, 255, .06) }
/* THE MARK SITS ON ITS OWN (19 Sep 2026, Alex: "remove the squares round each
   logo... not required... and ensure all the logos are positioned the same so
   product switch is seamless"). The box used to be a bordered square holding a
   shape drawn in CSS; the brand system supplies real marks, each with its own
   gradient, so the border was framing artwork that does not need framing.
   Every size and offset lives HERE, once, for all three products: they were
   briefly set per page, which is precisely how the lockup would start
   shifting a pixel or two as you switch between them. Nothing in a product
   page should restate these. */
.vs-product .vs-mark {
  width: 24px; height: 24px; flex: 0 0 24px; display: block;
}
.vs-product .vs-mark img { width: 100%; height: 100%; display: block; object-fit: contain }
.vs-product b { font-weight: 700 }
.vs-product span.light { font-weight: 300; color: var(--ink-2, #A7ABC2) }
.vs-product .vs-caret { color: var(--ink-3, #6B7089); font-size: 11px }

.vs-menu {
  position: absolute; top: 46px; min-width: 232px; background: var(--bg-2, #13131F);
  border: 1px solid var(--line, rgba(255, 255, 255, .1)); border-radius: 14px; padding: 8px;
  box-shadow: 0 24px 50px rgba(0, 0, 0, .5); display: flex; flex-direction: column; gap: 2px; z-index: 60;
}
/* THE [hidden] SCAR, once for the whole frame. Any display value here
   outranks the UA's [hidden] rule, so an element marked hidden still paints:
   it bit the customise panel, then both header menus, then an empty actions
   pill (17 Sep 2026). Every component that sets display carries the override,
   and anything added below must join this list. */
.vs-menu[hidden], .vs-pill[hidden], .vs-search[hidden], .vs-seg[hidden],
.vs-product[hidden], .vs-logo[hidden], .vs-tag[hidden] { display: none }
.vs-menu a, .vs-menu button {
  display: flex; align-items: center; gap: 10px; text-align: left; text-decoration: none;
  background: none; border: 0; color: var(--ink, #fff); font-family: inherit; font-size: 13.5px;
  padding: 8px 10px; border-radius: 9px; cursor: pointer;
}
.vs-menu a:hover, .vs-menu button:hover { background: var(--bg-3, rgba(255, 255, 255, .06)) }
.vs-menu a[aria-current="page"] { color: var(--vs-product) }
.vs-menu .vs-soon { margin-left: auto; font-size: 10px; color: var(--ink-3, #6B7089) }
.vs-menu .vs-sep { height: 1px; background: var(--line, rgba(255, 255, 255, .1)); margin: 6px 4px }

/* ---- quick search: the same slot and shape in every product -------------- */
.vs-search {
  flex: 0 1 380px; min-width: 180px; height: var(--vs-control-h); box-sizing: border-box;
  background: var(--bg-2, #13131F); border: 1px solid var(--line, rgba(255, 255, 255, .1));
  border-radius: 99px; color: var(--ink, #fff); font-family: inherit; font-size: 14px; padding: 0 16px; outline: none;
  transition: border-color .15s, background-color .15s;
}
.vs-search::placeholder { color: var(--ink-3, #6B7089) }
.vs-search:focus { border-color: var(--vs-product); background: var(--bg-3, #1B1B2A) }

/* ---- the scope toggle: Movies/TV on Poster, the four scopes on Social ----- */
.vs-seg {
  display: inline-flex; height: var(--vs-control-h); box-sizing: border-box; padding: 3px; gap: 2px;
  background: var(--bg-2, #13131F); border: 1px solid var(--line, rgba(255, 255, 255, .1)); border-radius: 99px;
}
.vs-seg button {
  background: none; border: 0; border-radius: 99px; padding: 0 18px; cursor: pointer;
  color: var(--ink-2, #A7ABC2); font-family: inherit; font-size: 13.5px; font-weight: 600; white-space: nowrap;
}
/* .active as well as .on: Poster's page JS marks its mode buttons .active,
   and an alias here keeps the file the single source rather than each
   product carrying a local line (Poster Vybe's ask, 17 Sep) */
/* Midnight on a solid product accent, per the brand system's interface
   standards: it measures 5.55 / 5.33 / 7.87 to 1 for pink, blue and teal,
   and says white on these colours is worse. */
.vs-seg button.on, .vs-seg button.active { background: var(--vs-product); color: #0B0B14 }

/* ---- actions ------------------------------------------------------------ */
.vs-pill {
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; flex: none;
  height: var(--vs-control-h); padding: 0 14px;
  background: var(--bg-3, rgba(255, 255, 255, .06)); border: 1px solid var(--line, rgba(255, 255, 255, .1));
  border-radius: 99px; color: var(--ink, #fff); font-family: inherit; font-size: 12.5px; cursor: pointer;
}
.vs-pill:hover { border-color: var(--vs-product) }
.vs-avatar {
  width: var(--vs-control-h); height: var(--vs-control-h); flex: none; border-radius: 50%; border: 0; cursor: pointer; font-weight: 700; font-size: 13px;
  background: linear-gradient(135deg, var(--vs-purple), var(--vs-product)); color: #fff; font-family: inherit;
}
.vs-tag {
  font-family: var(--vs-font-body); font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-3, #6B7089); border: 1px solid var(--line, rgba(255, 255, 255, .12));
  border-radius: 99px; padding: 3px 9px;
}

/* Narrow: the bar keeps its brand cluster and actions on one line and wraps
   search and the scope toggle onto a second, rather than dropping controls
   the product needs. The bar stops being a fixed height there, so a page
   offsetting its content must use the element's height, not the token. */
@media (max-width: 1080px) {
  :root { --vs-bar-gap: 14px }
  .vs-search { flex-basis: 240px }
  .vs-seg button { padding: 0 12px; font-size: 13px }
}
@media (max-width: 860px) {
  .vs-bar { height: auto; flex-wrap: wrap; padding: 10px 16px; row-gap: 10px }
  .vs-bar .vs-search, .vs-bar .vs-seg { order: 9 }
  .vs-bar .vs-search { flex: 1 1 100% }
  .vs-tag { display: none }
}
@media (max-width: 640px) {
  :root { --vs-logo-h: 26px }
  .vs-product > button { font-size: 15px }
  .vs-seg button { padding: 0 10px }
}
/* PHONE (Alex, 17 Sep 2026: "the phone version can be simplified, think of
   it like an app"). Two rows and nothing else: the product lockup with the
   actions and the account on top, the search sharing the second row with the
   scope toggle. The Vybe Studio wordmark and its divider go, because on a
   phone the product IS the app and the switcher still carries the family.
   About 104px at 375 wide. Agreed shape across Poster and Social. */
@media (max-width: 520px) {
  :root { --vs-bar-gap: 10px }
  .vs-bar { padding: 10px 12px }
  .vs-logo, .vs-div { display: none }
  .vs-product > button { font-size: 16px; padding: 4px 6px }
  .vs-product .vs-caret { display: none }
  .vs-bar .vs-search { flex: 1 1 auto; min-width: 0 }
  .vs-seg button { padding: 0 12px; font-size: 12.5px }
}

/* ---- shared bar pills ---------------------------------------------------
   ONE DEFINITION FOR ALL THREE PRODUCTS (20 Sep 2026). Alex: "all 3 sites need
   similar structure and options."

   Poster grew these first -- an icon plus a count that hides at zero, for My
   Projects and Favourites -- and they were written into Poster's own <style>.
   Copying them into Social and Trailer would have meant three copies drifting
   apart, which is how the account menu ended up with four different names for
   the admin console. They live here instead, beside .vs-bar, and a product
   supplies only the icon. */
.vs-iconpill{
  display:inline-flex; align-items:center; gap:6px; font:inherit; font-weight:600; line-height:1;
  background:var(--bg-2); border:1px solid var(--line); color:var(--ink);
  padding:8px 12px; border-radius:999px; cursor:pointer;
}
.vs-iconpill:hover{border-color:var(--ink-3)}
.vs-iconpill svg{width:17px; height:17px; display:block}
/* The count is the whole label, so it must not shout when empty: hidden at
   zero, exactly as Poster's board and heart counts behave. */
.vs-iconpill .count{background:var(--accent); color:#0B0B14; border-radius:999px; padding:0 6px; font-size:11px; font-weight:700; line-height:1.6}
.vs-iconpill .count[hidden]{display:none}
/* Open state: the pill fills with the accent, matching Poster's
   body:has(#panel.open) treatment without needing :has support. */
.vs-iconpill.is-open{background:var(--accent); border-color:var(--accent); color:#0B0B14}
.vs-heart{color:#ff4d6d; font-size:16px; line-height:1}
.vs-iconpill.is-open .vs-heart{color:#fff}
