/* Orchestrator owned fixes. Loads last, after every other layer, so a rule
   can be corrected without editing a file an agent is still working in.

   Keep this file small. Anything that survives a round belongs in the layer
   that owns it. */

/* ---------------------------------------------------------------------
   Four elements painted outside the viewport at 390px, on three surfaces.
   Measured at rest, after the opening animation clears, skipping anything
   positioned or transformed:

     studio.html      A.link  "Manage cohorts"     right 442, 52px over
     studio.html      SPAN.n  "14 / 16 seats"      right 394,  4px over
     instructor.html  BUTTON  "Open next roster"   right 463, 73px over
     frontdesk.html   SPAN    "Full, 2 waitlisted" right 499, 109px over

   Three of the four share one cause: `.card__head` and `.page-head--row`
   are flex rows holding a title on the left and an action or a pill on the
   right, and neither wraps. On a wide screen there is room. On a phone the
   title takes the full line and the trailing item is pushed off the edge.

   Worth being precise about what this is NOT. It is not the opening
   animation, which scales .app to 1.045 for the first two seconds and made
   the sweep report these at the wrong size before it learned to wait. It is
   also not the `1fr` grid trap: the shell itself measures exactly 390 at
   rest. I changed `grid-template-columns` first, on the assumption that it
   was that trap, and it fixed nothing, so it was reverted rather than left
   in as a plausible looking no-op.

   The teardown records a named layout defect at 390px in 5 of 5 booking
   platforms, so this is the width the whole category gets wrong, and the
   front desk and instructor surfaces are the two most likely to be opened
   on a phone in an actual studio.
   --------------------------------------------------------------------- */
@media (max-width: 560px) {
  .card__head,
  .page-head--row,
  .page-head--row > .row {
    flex-wrap: wrap;
    row-gap: 8px;
  }

  /* the title should take the line and let the action drop below it,
     rather than both squeezing and the label truncating */
  .card__head > .card__title,
  .page-head--row > .page-head {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* the seat count overran by 4px, which is the gap plus its own padding
     rather than anything structural. Let it shrink instead of forcing it. */
  .seatline {
    flex-wrap: wrap;
    row-gap: 6px;
  }
  .seatline > .n {
    min-width: 0;
  }

  /* A tab row is a flex row that does not wrap, which was fine while every
     page had three tabs. The Tier 2 work put a fourth on the front desk,
     the reviews page and the calculator, and a fifth on marketing, and the
     front desk row then ran 3px past the edge at 390.

     Wrapping rather than scrolling on purpose: a scrolling tab strip hides
     tabs behind an edge with nothing to say they are there, and a person
     who cannot see a tab does not look for it. */
  .tabs {
    flex-wrap: wrap;
    row-gap: 4px;
  }
}

/* ---------------------------------------------------------------------
   The view transition overlay was swallowing clicks for ~300ms after
   every nav change.

   Found by the dead click sweep reporting 38 of 115 dead, down from 3 of
   90. Bisecting by blocking one script at a time pointed at motion.js,
   and timing it showed the controls come back to life between 200ms and
   400ms after the nav click. So the sweep was measuring during the
   transition, and its number was wrong.

   But the product was wrong too, just less. A person who clicks quickly
   after switching pages loses that click, and motion that eats input is
   the one thing motion must never do. The snapshot pseudo elements are
   painted on top of the page and are hit testable by default, so they
   take the click and nothing downstream ever hears it.

   They are decoration. They should never be a target.
   --------------------------------------------------------------------- */
::view-transition,
::view-transition-group(*),
::view-transition-image-pair(*),
::view-transition-old(*),
::view-transition-new(*) {
  pointer-events: none;
}

/* ---------------------------------------------------------------------
   The change bar.

   355 choosers and 45 toggles all highlighted themselves on click and
   then said nothing. A studio owner picks "Satellite" and gets no answer
   to the only question they have: what did that do, and what happens
   when I close this? That is what "when I click it, they need to
   actually work" means, and it is a property of the panel rather than
   something to author 400 times.

   Sits at the foot of the panel, only once something has changed.
   --------------------------------------------------------------------- */
.pn__dirty {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  padding: 12px 20px;
  border-top: 1px solid var(--line);
  background: var(--amber-050, #fdf6e7);
  animation: pnDirtyIn var(--m-fast, 150ms) var(--e-out, ease-out) both;
}

@keyframes pnDirtyIn {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pn__dirty {
    animation: none;
  }
}

.pn__dirty__n {
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink, #23201c);
  min-width: 0;
}

.pn__dirty__a {
  display: flex;
  gap: 8px;
  flex: none;
}

/* ---------------------------------------------------------------------
   The topbar, at the widths people actually have.

   v2.css already fixed the horizontal overflow once by letting the search
   shrink. What it could not fix is that the two labels in this bar are
   bare text nodes, so when the box got narrow the only thing the text
   could do was wrap. Measured at 1280 before this:

     studio.html      .search 94px tall inside a 62px bar
     instructor.html  .search 114px
     frontdesk.html   .search 94px, bar overflows its own width by 70px
     member.html      switcher 62px, bar overflows by 70px

   And at 1440, a MacBook Air, "Search clients, classes, cohorts" broke
   across three lines on three surfaces. This bar is in the frame of every
   screenshot of this product, so it is the first thing anyone judging it
   sees go wrong.

   polish.py now wraps both labels, in `.search__t` and `.sw__t`, which is
   what makes the rules below possible at all.
   --------------------------------------------------------------------- */
.topbar > * {
  min-width: 0;
}

/* The switcher never shrinks. It is the answer to "which studio am I
   looking at", which is the one question this bar has to answer before
   any other, and a truncated "Core &..." answers it worse than nothing.
   The search absorbs the squeeze instead, and then gets out of the way. */
.topbar .studio-switcher {
  flex: none;
  min-width: 0;
  white-space: nowrap;
}

.topbar .studio-switcher > span:not(.dot),
.topbar .sw__t {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar .search {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 420px;
  white-space: nowrap;
  overflow: hidden;
}

.topbar .search svg {
  flex: none;
}

.topbar .search__t {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Below this the bar cannot hold a legible placeholder as well as the
   actions, and a clipped half sentence reads worse than no sentence. The
   box keeps its click target and becomes the magnifier it always was. */
@media (max-width: 1520px) {
  .topbar .search__t {
    display: none;
  }
  .topbar .search {
    flex: none;
    max-width: none;
    padding: 8px 10px;
  }
}

/* Under 1000 the studio name goes too, leaving the initials tile. Every
   surface names its studio again in the sidebar foot, so nothing is lost.

   1120 first, which took the name off at 1024. That is a real laptop, the
   bar has room for it there once the duplicate view badge is gone, and
   which studio you are looking at is the first question this bar answers.
   It also made the tour's own check fail: a 68px switcher measured during
   the opening animation clipped below the 100px floor where a 196px one
   had not, which is a check timing weakness rather than a product fault,
   and is fixed separately in tour-engine.mjs. */
@media (max-width: 1000px) {
  .topbar .studio-switcher > span:not(.dot),
  .topbar .sw__t {
    display: none;
  }
}

/* The bar said the same thing twice. `.vc-badge` reads "Studio view" and
   the role control 300px to its right reads "Studio view" as well, with
   the account name under it. Two components, one fact, 110px apart.

   The caption keeps its scope words, "one location", "today only", "your
   own account only", which are the part the role control does not carry
   and which three tour steps point at, so the element stays. Only the
   duplicate label goes. This is 1.8's rule, "must not be shown the same
   thing twice", applied to the one strip of the product that is in the
   frame of every screen. */
.topbar .view-caption .vc-badge {
  display: none;
}

/* Below 1400 the two widest actions drop to their icons. Both are still
   there, still in the same place, still one click. "Take the tour" is the
   entry point to the whole guided pass so it never disappears, and the
   route home is the answer to 1.9, so that never disappears either. */
@media (max-width: 1400px) {
  .topbar .tour-launch__label,
  .topbar .vswitch__home span,
  .topbar .vswitch__id {
    display: none;
  }
  .topbar .tour-launch,
  .topbar .vswitch__home {
    padding-left: 9px;
    padding-right: 9px;
  }
}

/* At 390 the bar still ran 41px past the right edge on four surfaces,
   which is the one overflow `verifyall.mjs` had been reporting all along
   and which nothing had got to because that file aborted earlier in its
   own run. The launcher goes here rather than any of the four controls
   around it, because TOUR.md already puts phone width out of scope for
   the tour: at 390 the opening sequence is still moving the topbar
   seconds after load, so step 1 points at something the product has not
   finished bringing on screen. Hiding a control that cannot work well
   there is honest. The tour is still reachable from Settings. */
@media (max-width: 560px) {
  .topbar {
    padding-left: 10px;
    padding-right: 10px;
    gap: 8px;
  }
  .topbar .tour-launch {
    display: none;
  }
  /* an empty box holding one magnifier, taking 44px including its gap,
     on a bar with 4px to spare. The sidebar's own search is two taps
     away and is the one people use. */
  .topbar .search {
    display: none;
  }
  .topbar-actions {
    gap: 4px;
  }
  .topbar .btn-ai {
    padding-left: 8px;
    padding-right: 8px;
  }
}

/* ---------------------------------------------------------------------
   The settings category rail.

   `.setnav` was authored into all four settings fragments and no CSS was
   ever written for it, which SETTINGS-MAP.md recorded as an open item for
   whoever owns the CSS. The result is not a slightly plain rail. `svg` in
   `app.css` sets `display:block` and no size, and only `.btn svg` is ever
   constrained, so each of the nine category icons painted at its natural
   box: roughly 190px tall. Nine of them. The whole left column of the
   settings page was a column of giant line drawings with a caption under
   each one. Measured on studio.html at 1440 wide before this block.

   That is D3 in his own words, "the UI doesn't look best, it's very
   complicated," and it is the reason a settings page that reads well in
   the markup looked broken on the screen.

   `.tab` is reused here on purpose, so `app.js` owns the switching and
   there is no second routing system. What that inherits is horizontal tab
   styling: a transparent bottom border, a negative bottom margin and an
   underline for the active state. All three are wrong stacked vertically,
   so they are undone here rather than in `app.css`, which the horizontal
   tabs everywhere else still need.
   --------------------------------------------------------------------- */
.setnav {
  position: sticky;
  top: calc(var(--topbar-h) + 12px);
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
}

.setnav > .tab {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 11px;
  border: 0;
  border-radius: var(--r-sm);
  margin: 0;
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--ink-2);
  background: transparent;
}

.setnav > .tab svg {
  flex: none;
  width: 16px;
  height: 16px;
  stroke: currentColor;
  opacity: 0.72;
}

.setnav > .tab:hover {
  color: var(--ink);
  background: var(--surface-2);
}

.setnav > .tab.active {
  color: var(--plum);
  background: var(--plum-050);
  font-weight: 650;
}

.setnav > .tab.active svg {
  opacity: 1;
}

/* One category is open at a time and its screen is long. Without this the
   rail scrolls away and there is no way back to the other eight without
   scrolling to the top of a 3,000px page. */
@media (max-width: 900px) {
  .setnav {
    position: static;
    display: flex !important;
    flex-wrap: wrap;
  }
  .setnav > .tab {
    width: auto;
  }
}

/* ---------------------------------------------------------------------
   Studio grid at scale. `.grid.cols-4` is four fixed columns, which is
   fine at 8 studios and 20 rows of scrolling at 80. Let the column count
   follow the width so it wraps vertically in a predictable way, and let
   grid.js cap what is shown. See grid.js for why the cards are a summary
   rather than the list.
   --------------------------------------------------------------------- */
.grid.cols-4:has(.studio-card) {
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.studio-card--over {
  display: none;
}

.gridmore {
  margin-top: 12px;
}
