/* ─────────────────────────────────────────────────────────────
   Crew Tower - feature previews

   Six of the features carry a preview: a small, cropped fragment of
   the real notch panel sitting in a softly tinted box, with a bold
   lead-in and a sentence underneath. The rest of the features stay
   as a compact text row below.

   ── what a fragment is ────────────────────────────────────────
   Not a screenshot and not the whole screen: only the part of the
   notch UI the feature is about, drawn in the DOM the same way the
   hero scene is. The brand marks, the pips and the status colours
   are the app's own, pulled from the tokens hero-demo.css publishes
   at :root, so there is exactly one copy of every value the app
   also owns.

   ── how it is sized ───────────────────────────────────────────
   The hero is authored in fixed design pixels and scaled with a
   transform, because it has to hold one screen's geometry. A crop
   has no geometry to preserve, so these are fluid instead: .fx-box
   is a container, and every size inside a fragment is in em off one
   container-relative font size. Nothing is transform-scaled, so
   nothing goes soft.

   ── motion ────────────────────────────────────────────────────
   Cards reveal on scroll (features-demo.js adds .is-in, staggered),
   and each fragment plays one beat when it lands: Allow is pressed,
   the tab hands over, an option is picked, the rings draw. Every
   beat runs once. Under prefers-reduced-motion everything is simply
   present, in its resolved state.

   Namespaced fx- to stay out of both styles.css and hero-demo.css.
   ───────────────────────────────────────────────────────────── */

.fx-grid, .fx-grid *, .fx-grid *::before, .fx-grid *::after,
.fx-more, .fx-more * { box-sizing: border-box; }

/* ── the grid of preview cards ────────────────────────────────── */

.fx-grid {
  display: grid;
  gap: 40px 24px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.fx-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Hiding is opt-in, never the default. features-demo.js sets .is-revealing
   on the grid as its first act, so a page with no JS - or with a script
   that failed - shows every card, already resolved, instead of six blank
   boxes. Nothing here is content the reveal is allowed to withhold. */
.fx-grid.is-revealing .fx-card {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity .6s ease, transform .6s cubic-bezier(.22, 1, .28, 1);
}
.fx-grid.is-revealing .fx-card.is-in { opacity: 1; transform: none; }

.fx-copy {
  margin: 0;
  font-size: 15px;
  line-height: 1.55;
  color: var(--slate);
}
.fx-copy b {
  font-weight: 600;
  color: var(--ink);
}

/* ── the tinted box ───────────────────────────────────────────── */
/* The tint is the product's own wallpaper, blurred right down and
   washed out: the same artwork that glows behind the hero screen, so
   a preview reads as a detail lifted out of that scene rather than a
   decoration invented for this section. */

.fx-box {
  container-type: inline-size;
  position: relative;
  /* The boxes are one height across a row, so the copy under them starts on
     one line. The aspect gives them that; the cap stops a wide two-column
     layout from turning the extra width into dead air, because the fragment
     inside stops growing once its type hits the 12px ceiling. */
  aspect-ratio: 3 / 2;
  max-height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5.5%;
  overflow: hidden;
  border-radius: var(--r-card-lg);
  background: var(--paper);
  border: 1px solid var(--edge);
  isolation: isolate;
}
.fx-box::before {
  content: "";
  position: absolute;
  inset: -30%;
  background: var(--ct-wallpaper);
  filter: blur(46px) saturate(120%);
  opacity: .55;
  z-index: -2;
}
/* a white wash over the wallpaper, so six of these in a row stay a
   background and the black fragments keep all the contrast */
.fx-box::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(160deg, rgba(255, 255, 255, .58), rgba(255, 255, 255, .78));
  z-index: -1;
}

/* ── a fragment of the notch ──────────────────────────────────── */
/* One font size drives everything inside; every other size is in em
   off it, so a fragment fills its box at any card width without ever
   being scaled. */

.fx-frag {
  position: relative;
  width: 100%;
  font-family: var(--font-ui);
  /* Capped at 12px because the longest real strings in these fragments -
     the question, and "Running tests · 128 passed, 0 failed" - have to
     land whole. A crop that ellipsises its own subject explains nothing. */
  font-size: clamp(8.5px, 4.3cqw, 12px);
  line-height: 1.3;
  color: #fff;
  -webkit-font-smoothing: antialiased;
}

/* The panel's own surface: black, with the app's card radius. */
.fx-panel {
  padding: .7em .8em;
  border-radius: 1.5em;
  background: #000;
  box-shadow: 0 1.2em 2.6em rgba(16, 15, 18, .3), 0 .3em .8em rgba(16, 15, 18, .16);
}
/* A crop of a control cluster rather than of a row: the panel hugs what it
   holds instead of stretching a couple of buttons across the whole width. */
.fx-panel-fit { width: max-content; max-width: 100%; margin: 0 auto; }

/* one session's row inside the panel */
.fx-row {
  padding: .75em .9em;
  border-radius: 1.1em;
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 0 0 1px transparent;
}
.fx-row + .fx-row { margin-top: .45em; }
.fx-row-perm { box-shadow: inset 0 0 0 1px rgba(255, 159, 46, .55); }
.fx-row-ask  { box-shadow: inset 0 0 0 1px rgba(89, 166, 255, .55); }

.fx-head { display: flex; align-items: flex-start; gap: .8em; }
.fx-main { flex: 1; min-width: 0; }
.fx-top  { display: flex; align-items: center; gap: .5em; }
.fx-spacer { flex: 1; }

.fx-project { font-size: 1.08em; font-weight: 600; letter-spacing: -.01em; color: #fff; }
.fx-elapsed { font-size: .86em; color: rgba(255, 255, 255, .45); white-space: nowrap; }

.fx-status {
  margin-top: .3em;
  font-family: var(--font-mono);
  font-size: .88em;
  color: rgba(255, 255, 255, .55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fx-status-attention { color: var(--ct-attention); }
.fx-status-asking    { color: var(--ct-asking); }

.fx-detail { padding-left: 2.4em; margin-top: .55em; }

/* The brand mark and its pip are hero-demo.css's, re-sized: the mask,
   the tints and the pip palette are the app's roster, declared once. */
.fx-frag .ct-agent { --ct-agent-size: 1.8em; margin-top: .1em; }
.fx-frag .ct-agent-pip { box-shadow: 0 0 0 .13em #000; }

/* ── the risk callout ─────────────────────────────────────────── */

.fx-risk {
  display: flex;
  align-items: center;
  gap: .45em;
  font-size: .9em;
  font-weight: 500;
  color: var(--ct-high);
}
.fx-risk svg { width: 1em; height: 1em; flex: none; }

.fx-code {
  margin-top: .5em;
  padding: .6em .7em;
  border-radius: .6em;
  background: rgba(255, 255, 255, .07);
  font-family: var(--font-mono);
  font-size: .9em;
  color: rgba(255, 255, 255, .85);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── buttons ──────────────────────────────────────────────────── */

.fx-btns { display: flex; align-items: center; gap: .45em; margin-top: .6em; }
/* the scope crop opens on the buttons, with no row above them to clear */
.fx-panel > .fx-btns:first-child { margin-top: 0; }
.fx-btn {
  display: flex;
  align-items: center;
  gap: .35em;
  height: 2em;
  padding: 0 .8em;
  border-radius: 1em;
  font-size: .9em;
  font-weight: 600;
  white-space: nowrap;
  color: rgba(255, 255, 255, .85);
  background: rgba(255, 255, 255, .12);
}
.fx-btn svg { width: .9em; height: .9em; flex: none; }
.fx-btn-allow {
  color: rgba(0, 0, 0, .9);
  background: var(--ct-attention);
  padding-right: 0;
}
.fx-btn-allow .fx-split { width: 1px; height: 1.15em; margin-left: .65em; background: rgba(0, 0, 0, .2); }
.fx-btn-allow .fx-chev { display: flex; align-items: center; padding: 0 .6em; }

/* ── the Allow scope menu ─────────────────────────────────────── */
/* Labels are SessionRowView's own: the split button is "once", the menu
   carries the two durable scopes. */

.fx-menu {
  width: 12.5em;
  margin: .55em 0 0 3.6em;
  padding: .3em;
  border-radius: .85em;
  background: rgba(38, 38, 42, .98);
  box-shadow: 0 1em 2.4em rgba(0, 0, 0, .5), inset 0 0 0 1px rgba(255, 255, 255, .09);
}
.fx-menu div {
  display: flex;
  align-items: center;
  gap: .5em;
  height: 1.9em;
  padding: 0 .6em;
  border-radius: .55em;
  font-size: .92em;
  color: rgba(255, 255, 255, .9);
  white-space: nowrap;
}
.fx-menu i { width: .35em; height: .35em; border-radius: 50%; background: var(--ct-attention); flex: none; }

/* ── question options ─────────────────────────────────────────── */

.fx-opt {
  display: flex;
  align-items: center;
  gap: .55em;
  padding: .5em .6em;
  border-radius: .7em;
  background: rgba(255, 255, 255, .10);
  box-shadow: inset 0 0 0 1px transparent;
}
.fx-opt + .fx-opt { margin-top: .35em; }
.fx-opt-no {
  width: 1.35em;
  height: 1.35em;
  border-radius: 50%;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .85em;
  font-weight: 700;
  color: rgba(255, 255, 255, .8);
  background: rgba(255, 255, 255, .15);
}
.fx-opt-label {
  font-size: .92em;
  color: rgba(255, 255, 255, .9);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* the option that was picked - the beat below animates into exactly this */
.fx-opt-picked { background: rgba(89, 166, 255, .16); box-shadow: inset 0 0 0 1px rgba(89, 166, 255, .6); }
.fx-opt-picked .fx-opt-no { background: var(--ct-asking); color: rgba(0, 0, 0, .85); }

/* ── the terminal tab strip ───────────────────────────────────── */
/* Under the notch fragment, so the pair says "click here, land there".
   Glass over the wallpaper, exactly as the hero's terminal is. */

.fx-term {
  margin-top: .9em;
  border-radius: .7em;
  overflow: hidden;
  background: rgba(22, 20, 30, .82);
  box-shadow: 0 .8em 1.8em rgba(16, 15, 18, .26), inset 0 1px 0 rgba(255, 255, 255, .12);
}
.fx-tabs { display: flex; gap: 1px; padding: .35em .35em 0; }
.fx-tab {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 1.9em;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: .45em .45em 0 0;
  font-size: .8em;
  color: rgba(255, 255, 255, .45);
  background: transparent;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 .5em;
}
.fx-tab::after {
  content: "";
  position: absolute;
  left: .5em; right: .5em; bottom: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--ct-high);
  opacity: 0;
}
.fx-tab-on { background: #1d2129; color: rgba(255, 255, 255, .95); }
.fx-tab-on::after { opacity: 1; }
.fx-term-body {
  height: 2.6em;
  padding: .5em .7em;
  background: #0d1015;
  font-family: var(--font-mono);
  font-size: .82em;
  color: rgba(238, 235, 250, .78);
  white-space: nowrap;
  overflow: hidden;
}

/* ── the context ring ─────────────────────────────────────────── */
/* Bands are ContextUsage.level: normal under 70%, high under 90%,
   critical at or above it. Colours are ContextIndicator's. */

.fx-ring { display: flex; align-items: center; gap: .35em; }
.fx-ring svg { width: 1.15em; height: 1.15em; display: block; }
.fx-ring circle { fill: none; stroke-width: 3; }
.fx-ring-bg { stroke: rgba(255, 255, 255, .14); }
.fx-ring-fg { stroke: var(--ct-ok); stroke-linecap: round; }
.fx-ring-num {
  font-size: .86em;
  font-weight: 500;
  color: rgba(255, 255, 255, .5);
  font-variant-numeric: tabular-nums;
}
.fx-ring-high .fx-ring-fg { stroke: var(--ct-high); }
.fx-ring-high .fx-ring-num { color: var(--ct-high); }
.fx-ring-crit .fx-ring-fg { stroke: var(--ct-crit); }
.fx-ring-crit .fx-ring-num { color: var(--ct-crit); }

/* Three whole rows at once is the tallest fragment here, and it has to be
   whole rows: a session's card carries its status line, and a preview that
   dropped it would be showing a row the app never draws. The type comes
   down a step instead. */
.fx-frag-dense { font-size: .88em; }

/* ── the pointer ──────────────────────────────────────────────── */

.fx-cursor {
  position: absolute;
  width: 1.15em;
  height: 1.6em;
  opacity: 0;
  pointer-events: none;
  z-index: 5;
  filter: drop-shadow(0 .1em .2em rgba(0, 0, 0, .55));
}
.fx-cursor svg { width: 100%; height: 100%; display: block; }

/* ── the beats ────────────────────────────────────────────────── */
/* Each fires once, on the card landing. `both` holds the end state, so
   a fragment that has played reads as a finished interaction rather
   than snapping back to its opening frame. */

.fx-card.is-in .fx-beat-press  { animation: fx-press 2.4s .5s both; }
@keyframes fx-press {
  0%, 30%   { transform: none; box-shadow: none; }
  38%       { transform: scale(.94); box-shadow: 0 0 0 .3em rgba(255, 159, 46, .3); }
  46%, 100% { transform: none; box-shadow: none; }
}

.fx-card.is-in .fx-beat-cursor-allow { animation: fx-cursor-allow 2.4s .2s both; }
@keyframes fx-cursor-allow {
  0%        { opacity: 0; transform: translate3d(4.5em, 3.2em, 0); }
  18%       { opacity: 1; transform: translate3d(4.5em, 3.2em, 0); }
  40%, 78%  { opacity: 1; transform: translate3d(0, 0, 0); }
  100%      { opacity: 0; transform: translate3d(.4em, .5em, 0); }
}

.fx-card.is-in .fx-beat-pick    { animation: fx-pick 2.2s .55s both; }
.fx-card.is-in .fx-beat-pick-no { animation: fx-pick-no 2.2s .55s both; }
@keyframes fx-pick {
  0%, 40%   { background: rgba(255, 255, 255, .10); box-shadow: inset 0 0 0 1px rgba(89, 166, 255, 0); }
  55%, 100% { background: rgba(89, 166, 255, .16);  box-shadow: inset 0 0 0 1px rgba(89, 166, 255, .6); }
}
@keyframes fx-pick-no {
  0%, 40%   { background: rgba(255, 255, 255, .15); color: rgba(255, 255, 255, .8); }
  55%, 100% { background: var(--ct-asking); color: rgba(0, 0, 0, .85); }
}

.fx-card.is-in .fx-beat-tab-off  { animation: fx-tab-off 2.4s .5s both; }
.fx-card.is-in .fx-beat-tab-on   { animation: fx-tab-on  2.4s .5s both; }
.fx-card.is-in .fx-beat-pane-off { animation: fx-fade-out 2.4s .5s both; }
.fx-card.is-in .fx-beat-pane-on  { animation: fx-fade-in  2.4s .5s both; }
@keyframes fx-tab-off {
  0%, 40%   { background: #1d2129; color: rgba(255, 255, 255, .95); }
  55%, 100% { background: rgba(29, 33, 41, 0); color: rgba(255, 255, 255, .45); }
}
@keyframes fx-tab-on {
  0%, 40%   { background: rgba(29, 33, 41, 0); color: rgba(255, 255, 255, .45); }
  55%, 100% { background: #1d2129; color: rgba(255, 255, 255, .95); }
}
@keyframes fx-fade-out { 0%, 40% { opacity: 1; } 52%, 100% { opacity: 0; } }
@keyframes fx-fade-in  { 0%, 44% { opacity: 0; } 56%, 100% { opacity: 1; } }
/* the underline rides with its tab */
.fx-card.is-in .fx-beat-tab-off::after { animation: fx-fade-out 2.4s .5s both; }
.fx-card.is-in .fx-beat-tab-on::after  { animation: fx-fade-in  2.4s .5s both; }

/* The second pane is stacked over the first, so the strip keeps one
   height. Resolved is "the tab you jumped to", which is pane two. */
.fx-term-body { position: relative; }
.fx-pane { position: absolute; inset: 0; padding: .5em .7em; opacity: 0; }
.fx-pane-on { opacity: 1; }

/* The rings draw to their value. The dash array is the circle's full
   circumference (r=7, so 43.98) and the offset is what is left of it,
   set per ring in the markup as --fx-dash. Resolved is the resting
   state; only a revealing grid winds them back to empty first. */
.fx-ring-fg { stroke-dasharray: 43.98; stroke-dashoffset: var(--fx-dash); }
.fx-grid.is-revealing .fx-ring-fg { stroke-dashoffset: 43.98; }
.fx-card.is-in .fx-ring-fg { animation: fx-draw 1.1s .35s cubic-bezier(.22, 1, .28, 1) both; }
@keyframes fx-draw {
  from { stroke-dashoffset: 43.98; }
  to   { stroke-dashoffset: var(--fx-dash); }
}

/* ── the remaining features, as a compact row ─────────────────── */
/* Everything the previews do not cover. Tighter than the tiles it
   replaces: no card, just a rule and a tight pair of lines, so it
   reads as the appendix to the previews rather than a second wall. */

.fx-more {
  margin-top: 72px;
  padding-top: 32px;
  border-top: 1px solid var(--edge);
  display: grid;
  gap: 28px 40px;
  /* the same track as the previews above, so the two blocks line up
     column for column instead of reading as separate grids */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.fx-more h3 {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -.015em;
  margin-bottom: 6px;
}
.fx-more p {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--slate);
}

/* ── narrow ───────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .fx-grid { gap: 32px; }
  /* One card across. A fixed aspect at full width is nearly all empty
     box, so here the box takes the height its fragment needs and the
     floor keeps the shortest of them from looking squashed. */
  .fx-box { aspect-ratio: auto; max-height: none; min-height: 190px; padding: 22px; }
  .fx-more { margin-top: 56px; gap: 24px; }
}

/* ── reduced motion ───────────────────────────────────────────── */
/* No reveal, no beats: every fragment is simply present, already in
   its resolved state. features-demo.js does not observe at all in this
   mode, so nothing here has to fight a class it might add. */

@media (prefers-reduced-motion: reduce) {
  .fx-grid.is-revealing .fx-card { opacity: 1; transform: none; transition: none; }
  .fx-card .fx-cursor { display: none; }
  .fx-grid .fx-ring-fg { stroke-dashoffset: var(--fx-dash); }
  .fx-card * { animation: none !important; }
}
