/* ────────────────────────────────────────────────────────────────────────────
   Index stripe: a live, rotating row of index prices.

   Self-contained on purpose. Everything here is scoped under .ixs, and every
   colour comes from a token that both stylesheets already define: main-dashboard.css
   for the dashboard, content-v2.css for content pages, each with its own light
   variant. That is what lets the component drop onto either surface without
   carrying a palette of its own, and why nothing below hard-codes a hex.

   MD3 Expressive, deliberately:
   · a tonal container rather than a flat bar, so the row reads as a surface
   · the expressive asymmetric shape on every cell, mirrored as it flips, which is
     shape morphing used where it means something (the row has turned over) rather
     than as decoration
   · emphasized decelerate on the way in, so a page arrives with weight and settles
   · a stagger across the row, so the flip reads as a board turning rather than a
     slide changing
   · real type roles: Label Medium for the name, Title Medium for the number, both
     scaled by vmin because this is read from across a room
   ──────────────────────────────────────────────────────────────────────────── */

.ixs {
  /* Per-instance knobs, so a host can retune the component without editing it.

     Deliberately slow. The first pass turned a page over in about a quarter of a
     second, which on a wall read as a flicker rather than a movement: too quick to
     follow, quick enough to catch the eye. A turn now takes most of a second and a
     half, which is long enough to see it happen and then stop caring. */
  --ixs-stagger: 60ms;
  --ixs-in:  820ms;
  --ixs-out: 420ms;

  /* How hard the direction colour reads on a cell's edge. Separate knobs because
     the right answer depends on the room: a screen across an office wants more than
     a monitor on a desk, and both are this component on the same page. Turn them
     down to nothing and the row goes back to a plain outline. */
  --ixs-edge: 55%;
  --ixs-glow: 14%;
  --ixs-glow-size: clamp(.25rem, .7vmin, .65rem);

  /* MD3 emphasized. The long tail is the whole point: a page arrives quickly, then
     takes its time settling, which is what separates a movement from a cut. The
     outgoing curve is a softened accelerate rather than the sharp one, because a
     cell that snaps away leaves a hole no amount of easing on the way in can hide. */
  --ixs-ease-in:  cubic-bezier(0.2, 0, 0, 1);
  --ixs-ease-out: cubic-bezier(0.4, 0, 0.7, 0.2);

  display: block;
  background: var(--md-surf-2);
  border-top: 1px solid var(--md-outline-var);
  border-radius: 1.25rem 1.25rem 0 0;
  padding: clamp(.3rem, .7vmin, .7rem) clamp(.4rem, 1vmin, 1rem);
  overflow: hidden;
}

/* Flex rather than fixed columns, so cells share the row by fraction. A zone whose
   last page is short simply lets its remaining cell spread, instead of leaving a
   hole where the missing one would have been. */
.ixs-track {
  display: flex;
  align-items: stretch;
  gap: clamp(.3rem, .8vmin, .8rem);
}

/* ── Pinned pair, then the regions ───────────────────────────────────────────
   The pinned cells hold their own width and never rotate. The zones share what is
   left, weighted by how many cells each shows, so a two-cell zone is not squeezed
   next to a three-cell one. */
.ixs-pinned {
  display: flex;
  align-items: stretch;
  gap: clamp(.3rem, .8vmin, .8rem);
  flex: 0 1 auto;
}

.ixs-pinned .ixs-cell { flex: 0 1 auto; min-width: clamp(4rem, 9vmin, 8rem); }

/* A hairline before each region rather than a box around it. The row already carries
   eight tiles; three more containers would turn a stripe into a table. */
.ixs-zone {
  display: flex;
  align-items: center;
  gap: clamp(.3rem, .7vmin, .7rem);
  flex: 1 1 0;
  min-width: 0;
  padding-left: clamp(.35rem, .9vmin, .9rem);
  border-left: 1px solid var(--md-outline-var);
}

/* The region tag. Tracked out so it reads as a spine at the side of its group rather
   than as another label competing with the market names. Sized well below them: it is
   orientation, not content. */
.ixs-zone-label {
  flex: 0 0 auto;
  font-size: clamp(.5rem, .92vmin, .85rem);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--md-primary);
  white-space: nowrap;
}

.ixs-zone-cells {
  display: flex;
  align-items: stretch;
  gap: clamp(.3rem, .8vmin, .8rem);
  flex: 1 1 0;
  min-width: 0;
}

/* A plain rounded rectangle, and it stays one. The shape does not morph on the flip:
   the movement carries the transition on its own, and a tile that changes silhouette
   mid-turn reads as a wobble rather than as expression.

   Centred, not left aligned. Left aligned, every value sat against the edge of a
   box several times wider than itself and the row read as mostly empty.

   The tile is a state layer rather than --md-surf-3. In dark those two surface
   tokens are #14242F and #172834, three points apart, so a tile painted with the
   nominally lighter one was invisible against its own container. A percentage of
   the foreground colour lifts reliably in both themes and hard-codes nothing. */
.ixs-cell {
  /* The cell's own direction colour, set by the rules below and read by the border
     and the glow. One variable rather than repeating the whole edge treatment per
     state, which is what kept volatility and sentiment from needing their own copy
     of it. Unset until a value arrives, so a placeholder cell has a plain edge. */
  --ixs-dir: var(--md-outline-var);

  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .15em;
  min-width: 0;
  padding: clamp(.25rem, .8vmin, .7rem) clamp(.35rem, .9vmin, .8rem);
  border-radius: clamp(.3rem, .7vmin, .6rem);
  background: color-mix(in srgb, var(--md-on-surface) 7%, transparent);

  /* The edge carries the direction. Read from across a room the numbers are already
     too small, but a red edge against a green one is not, so the row stays useful
     further back than its own text is legible.

     Never the only carrier: the sign and the percentage are in the delta beside the
     value, which is what keeps this readable for anyone who cannot separate red from
     green. The edge is reinforcement, not the message. */
  border: 1px solid color-mix(in srgb, var(--ixs-dir) var(--ixs-edge), transparent);
  box-shadow: 0 0 var(--ixs-glow-size) color-mix(in srgb, var(--ixs-dir) var(--ixs-glow), transparent);

  /* Colour eases on its own clock, slower than the flip. A price crossing from up to
     down shifts the edge gently instead of snapping, and a page turn hides its own
     colour change behind the fade anyway. */
  transition:
    opacity      var(--ixs-in) var(--ixs-ease-in),
    transform    var(--ixs-in) var(--ixs-ease-in),
    border-color 600ms var(--ixs-ease-in),
    box-shadow   600ms var(--ixs-ease-in);
}

/* Direction, for a price. Everything else about a cell's colour already funnels
   through these same attributes, so the edge needs no rules of its own. */
.ixs-cell[data-dir="up"]   { --ixs-dir: var(--gain); }
.ixs-cell[data-dir="down"] { --ixs-dir: var(--loss); }
.ixs-cell[data-dir="flat"] { --ixs-dir: var(--md-outline-var); }

/* Volatility inverts, for the same reason its delta does: a rising VIX is not good
   news, and an edge painted the same green as a rising index would say it was. */
.ixs-cell[data-kind="volatility"][data-dir="up"]   { --ixs-dir: var(--loss); }
.ixs-cell[data-kind="volatility"][data-dir="down"] { --ixs-dir: var(--gain); }

/* Sentiment has no direction, only a band, so the edge takes the band's colour. */
.ixs-cell[data-band="greed"]   { --ixs-dir: var(--gain); }
.ixs-cell[data-band="fear"]    { --ixs-dir: var(--loss); }
.ixs-cell[data-band="neutral"] { --ixs-dir: var(--amber); }

/* Value and change on one line, sharing a baseline so the smaller number sits on
   the larger one rather than floating beside it. Wraps rather than overflows when a
   cell is genuinely too narrow, which is what happens on a phone. */
.ixs-readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: .1em .45em;
  max-width: 100%;
}

/* A short last page leaves spare cells, and the script hides them. The rule above sets
   display, which outranks the [hidden] default, so without !important a hidden cell
   would keep its box and the row would end in empty tiles. */
.ixs-cell[hidden] { display: none !important; }

/* Label Medium. Uppercase with tracking, because at this size a market name is an
   identifier being scanned for, not a word being read. */
.ixs-label {
  font-size: clamp(.58rem, 1.1vmin, 1.05rem);
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--md-on-surface-var);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Title Medium. Tabular figures so a ticking price does not shuffle its own
   digits sideways, which on a wall reads as the whole row twitching. */
.ixs-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: clamp(.95rem, 2.15vmin, 2rem);
  line-height: 1.05;
  color: var(--md-on-surface);
  white-space: nowrap;
}

.ixs-delta {
  font-variant-numeric: tabular-nums;
  font-size: clamp(.6rem, 1.2vmin, 1.1rem);
  font-weight: 600;
  line-height: 1.05;
  white-space: nowrap;
}

.ixs-cell[data-dir="up"]   .ixs-delta { color: var(--gain); }
.ixs-cell[data-dir="down"] .ixs-delta { color: var(--loss); }
.ixs-cell[data-dir="flat"] .ixs-delta { color: var(--md-on-surface-var); }

/* Sentiment paints the value itself, since Fear and Greed has no change to colour
   and the word is the whole point. Amber for neutral, which is the token the rest
   of the site already uses for "neither". */
.ixs-cell[data-kind="sentiment"][data-band="greed"]   .ixs-value { color: var(--gain); }
.ixs-cell[data-kind="sentiment"][data-band="fear"]    .ixs-value { color: var(--loss); }
.ixs-cell[data-kind="sentiment"][data-band="neutral"] .ixs-value { color: var(--amber); }

/* Volatility never goes green. A rising VIX is not good news, and painting it with
   the same colour as a rising index would say it was. */
.ixs-cell[data-kind="volatility"] .ixs-value { color: var(--amber); }
.ixs-cell[data-kind="volatility"][data-dir="up"]   .ixs-delta { color: var(--loss); }
.ixs-cell[data-kind="volatility"][data-dir="down"] .ixs-delta { color: var(--gain); }

/* ── The flip ────────────────────────────────────────────────────────────────
   Out is short and accelerating, in is long and decelerating, which is what makes
   the row feel like it turned over rather than cross-faded. The stagger is set
   per cell by the script as --ixs-i, so the delay is the cell's own index. */
.ixs-cell.is-out {
  opacity: 0;
  transform: translateY(-26%) scale(.985);
  transition:
    opacity   var(--ixs-out) var(--ixs-ease-out),
    transform var(--ixs-out) var(--ixs-ease-out);
  transition-delay: calc(var(--ixs-i, 0) * var(--ixs-stagger));
}

.ixs-cell.is-in {
  opacity: 0;
  transform: translateY(26%) scale(.985);
  transition: none;
}

.ixs-cell.is-settled {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--ixs-i, 0) * var(--ixs-stagger));
}

/* A value that changed while its cell was standing still gets a brief lift rather
   than a flash. On a screen running for weeks, a flashing number becomes something
   the eye learns to ignore. */
@keyframes ixs-bump {
  0%   { transform: none; }
  35%  { transform: translateY(-12%); }
  100% { transform: none; }
}

.ixs-value.is-bumped {
  animation: ixs-bump 420ms var(--ixs-ease-in);
}

/* Motion is the presentation, never the information: with it suppressed the page
   still turns over, it just cuts instead of flipping. */
@media (prefers-reduced-motion: reduce) {
  .ixs-cell,
  .ixs-cell.is-out,
  .ixs-cell.is-in,
  .ixs-cell.is-settled {
    transition: none;
    transform: none;
  }
  .ixs-cell.is-out { opacity: 0; }
  .ixs-value.is-bumped { animation: none; }
}

/* ── On the market wall ──────────────────────────────────────────────────────
   The stripe runs along the bottom and stops at 90% of the width. The last tenth
   is where the Windows app draws its Feedback button, and a row of prices sliding
   under it would be unreadable exactly where it ended. */
body.is-market-wall .ixs {
  position: fixed;

  /* Lined up with the clock grid, not the viewport edge. The wall page pads itself
     by --cs-sm, so borrowing the same token puts the stripe's left edge directly
     under the first column of cards. A hard-coded offset would drift the moment that
     padding was retuned.

     right rather than width, so the reserved corner stays exactly what it is set to.
     With width: 90% from a left offset the row would have reached past that and slid
     under the app's Feedback button.

     The default is symmetric with the left edge, so a browser visitor gets a stripe
     that lines up with the clocks on both sides. The corner is only given away when
     the app says it is there, which is the rule below. */
  left: var(--cs-sm);
  right: var(--cs-sm);
  bottom: 0;
  z-index: 4;
}

/* The packaged app draws its Feedback button in the bottom right, so the stripe stops
   short of it. Set only when ?kiosk=1 reached the page, which is the app asking: a
   tenth of dead width in a browser is a gap, not a reservation. */
body.is-market-wall.is-app-host .ixs { right: 10%; }

/* ── On a phone ──────────────────────────────────────────────────────────────
   One region at a time, which the script arranges by hiding the others. Three
   regions will not fit: two pinned cells, three labels and six values on 375
   pixels leaves about fifty each, and a five figure index needs twice that, so
   they simply printed over one another.

   The zone rule sets display, which outranks the [hidden] default, so hiding a
   zone needs the same !important the cells already do. */
@media (max-width: 720px) {
  .ixs-zone[hidden] { display: none !important; }

  /* No caption reserve on a phone, and the app never runs at this width anyway. */
  body.is-market-wall.is-app-host .ixs { right: var(--cs-sm); }

  .ixs { padding: clamp(.25rem, 1.2vw, .5rem) clamp(.3rem, 1.4vw, .6rem); }

  .ixs-track,
  .ixs-pinned,
  .ixs-zone,
  .ixs-zone-cells { gap: clamp(.25rem, 1vw, .5rem); }

  /* The pinned pair holds two short readings and should not take the width a five
     figure index needs. */
  .ixs-pinned .ixs-cell { min-width: 0; }

  .ixs-zone { padding-left: clamp(.25rem, 1vw, .5rem); }
}

/* Room at the foot of the page so the last row of clocks is never behind the
   stripe. The stripe is fixed, so it takes no space of its own. */
body.is-market-wall .mkc-page {
  padding-bottom: clamp(3.2rem, 7vmin, 6rem);
}
