/* trade_canvas/static/assets/css/today.css
 *
 * Today tab -- the daily scorecard -- and the shared Today visual
 * (components/visuals/today_snapshot.html, C30) that is both the tab's hero
 * and movers and Home's "What's moving your portfolio?" band. Deliberately
 * thin: layout only. Every colour, radius, font and spacing value comes from
 * the tokens portfolio.html already defines (--bg-card-soft, --border-subtle,
 * --accent-green, --accent-red, --space-*, --fs-*) or the site-wide --ss-*
 * tokens (custom.css), so Today inherits the dashboard's look rather than
 * restating it. Card chrome reuses .pop2-card / .pop2-kicker / .pop2-btn and
 * the gain/loss classes .pop2-pos / .pop2-neg; a public page doesn't load the
 * app's card styles, so the visual states them for itself at the end
 * (`--showcase`).
 *
 * Mobile is the primary target: this is a daily check-in, so the hero value
 * and the day's change must both land above the fold on a phone.
 */

.today-hero {
  position: relative;
  overflow: hidden;
  transition: background-color 0.15s ease;
  padding: var(--space-lg, 16px) var(--card-pad-x, 16px);
}

/* Decorative portfolio-value line. Sits behind the numbers, weighted to the
   right so it stays clear of the text, and adds no height to the card --
   it is absolutely positioned, so the hero measures exactly as before. The
   left-to-right emergence is in the SVG gradients, not here. */
.today-hero__spark {
  position: absolute;
  transition: opacity 0.15s ease;
  /* An accent floating in the right half, not a chart filling it: inset from
     the bottom so there is air above and below, and it does not read as rising
     off the card's floor. Absolutely positioned against the hero, so it still
     adds no height. Where the ink starts is set by the gradient stops in the
     markup, not by this box. */
  right: 0;
  /* Explicit height, not top+bottom: with `height: auto` an SVG resolves from
     its own viewBox aspect ratio and overshoots the card. Deliberately short
     of the card's own height: the trend is context, and a chart that fills the
     card's vertical space reads as the subject rather than the backdrop. */
  bottom: 14%;
  height: 71%;
  /* Mobile: narrower and dimmer, so it never competes with the value for a
     reader holding the phone at arm's length. */
  width: 54%;
  opacity: 0.58;
  pointer-events: none;
  z-index: 0;
}

/* ── Build-in (C30; design guide, Motion) ───────────────────────────────
   Every load animation is scoped to `.is-animating` on the visual's root,
   which only today-snapshot.js adds -- the app on the tab's first open per
   browser session, a public page on every visit -- and removes once the last
   animation has ended. A CSS animation restarts whenever its element goes
   from display:none to shown, so unscoped, the Today tab replayed this on
   every re-show. Without the class (a re-show, reduced motion, a script that
   never ran) nothing is hidden: the visual is its end state.

   Text never animates in (design guide, Motion; founder 2026-10-09, C30):
   the value, the day's change and the mover rows are on screen from the
   first frame. What builds in is data: in the app the sparkline fades up and
   the bars grow; a public page grows only the bars. */
@media (prefers-reduced-motion: no-preference) {
  .today-snapshot-embed--app.is-animating .today-hero__spark {
    animation: todayHeroSparkIn 450ms cubic-bezier(0.16, 1, 0.3, 1) both;
  }

  /* The bars grow to the width the server set, 450ms each and 80ms apart in
     both profiles (C7's showcase timing). Omitting the `to` frame keeps that
     inline width authoritative, so the shared magnitude scale is untouched. */
  .today-snapshot-embed.is-animating .today-mover__meter-fill {
    animation: todayMeterGrow 450ms cubic-bezier(0.16, 1, 0.3, 1)
      calc(var(--today-row-index, 0) * 80ms) both;
  }

  /* Held at the first frame until the visual is half on screen; `both` keeps
     each element at its `from` state meanwhile. Last, so it outranks the
     shorthands above (which reset the play state). */
  .today-snapshot-embed.is-animating.is-waiting * { animation-play-state: paused; }
}

/* Emerges from the background: a fade and a 4px settle, nothing drawn. */
@keyframes todayHeroSparkIn {
  from { opacity: 0; transform: translateY(4px); }
}

@keyframes todayMeterGrow {
  from { width: 0; }
}

/* The shared identity's company line is a touch larger and brighter inside a
   mover row than it is in the Positions table, where it sits in a denser grid
   with sector and industry beneath it. Scoped to Today so the table is
   untouched; the ticker stays the primary mark at 14px/600. */
.today-mover .snapshot-company-name {
  font-size: 0.76rem;
  color: var(--ss-text-muted);
  opacity: 1;
}

/* The sparkline is the hero's link to Performance -- no extra button. The
   anchor covers the chart's own box on desktop; on small screens it shrinks to
   the bottom-right corner so it can never sit over the portfolio value. */
.today-hero__spark-link {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 52%;
  height: 66%;
  z-index: 2;
  /* Desktop only. At 390px the value and the day's change fill the card, so
     there is no empty region to put a tap target in -- an invisible link over
     the date would swallow taps meant for the text. Mobile reaches Performance
     from the tab bar, which is directly above. */
  display: none;
  cursor: pointer;
  border-radius: 10px;
  transition: filter 0.15s ease;
}

.today-hero:has(.today-hero__spark-link:hover) .today-hero__spark,
.today-hero:has(.today-hero__spark-link:focus-visible) .today-hero__spark {
  opacity: 1;
}

.today-hero__spark-link:focus-visible {
  outline: 2px solid var(--ss-focus-ring, rgba(169, 143, 248, 0.6));
  outline-offset: -2px;
}

/* Everything else in the hero sits above the chart. Both absolutely
   positioned layers are excluded -- giving the SVG `position: relative` would
   put it back in flow and add its intrinsic height to the card. */
.today-hero > *:not(.today-hero__spark):not(.today-hero__spark-link) {
  position: relative;
  z-index: 1;
}

.today-hero__label {
  margin-bottom: var(--space-xs, 4px);
}

.today-hero__value {
  width: fit-content;
  font-size: clamp(2.25rem, 11vw, 3rem);
  line-height: 1.05;
  margin-top: 2px;
  font-weight: var(--fw-bold, 700);
  color: var(--text-primary, #e6edf3);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* A descriptor, not a second headline: the page's own kicker language a step
   smaller and lighter than the "Portfolio Value" label above it. */
.today-hero__change-label {
  margin: 14px 0 2px;
  font-size: 11px;
  font-weight: var(--fw-semibold, 600);
  color: var(--ss-text-muted);
  letter-spacing: 0.06em;
}

.today-hero__change {
  width: fit-content;
  margin-top: 0;
  font-size: clamp(1rem, 4.5vw, 1.15rem);
  font-weight: var(--fw-semibold, 600);
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.today-hero__change-period {
  color: var(--ss-text-secondary);
  font-weight: var(--fw-regular, 400);
  font-size: var(--fs-body, 14px);
}

.today-hero__change.is-flat { color: var(--ss-text-secondary); }

/* The day change and investment growth are gain/loss figures (design guide,
   Semantic insight color). Stated here because their own colour rules above
   and below come later than portfolio.html's .pop2-pos / .pop2-neg and would
   otherwise paint them white -- and a public page has no .pop2-pos at all. */
.today-hero__change.pop2-pos,
.today-investment-stat__value.pop2-pos { color: var(--accent-green, #34d399); }
.today-hero__change.pop2-neg,
.today-investment-stat__value.pop2-neg { color: var(--accent-red, #fb8a8a); }

.today-hero__note {
  margin: var(--space-sm, 8px) 0 0;
  font-size: var(--fs-small, 12px);
  color: var(--ss-text-muted);
}

/* Arrival highlight for a holding opened from a Today mover row. Lives here
   rather than in positions.css because Today owns the hand-off; the Positions
   table itself is untouched. A phone marks its list item, the holding it can
   see (the table is not displayed below 860px). */
.positions-row-focus > td,
.pm-row.positions-row-focus {
  background: rgba(111, 66, 193, 0.22);
  transition: background-color 900ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .positions-row-focus > td,
  .pm-row.positions-row-focus { transition: none; }
}

/* ── Today's movers ──────────────────────────────────────────────────
   A ranked list with visual weighting, not an analytical chart. Every meter
   starts at the same left edge and its length is |contribution| against the
   largest in the list; colour carries the sign, so +$738 and -$738 draw the
   same length in opposite colours. No axis, no gridlines, no library.

   Rows are ordered by *signed* contribution, so the column of meters falls
   from the biggest gains through to the biggest losses and the shape of the
   list is itself a read on the day.

   The holding identity is not restyled here: it comes from the shared
   `holding_identity` macro and keeps the Positions table's classes. */

.today-movers-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md, 12px);
}

/* Footer: metadata on the left, the way through to Positions on the right. */
.today-movers-card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px var(--space-md, 12px);
  padding: var(--space-sm, 8px) var(--card-pad-x, 16px) var(--space-md, 12px);
}

.today-movers-card__note {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.45;
  letter-spacing: 0.01em;
  /* The meta token: a note that explains why holdings are missing from the
     list above it must stay readable, not faint. */
  color: var(--ss-text-muted);
}

.today-movers-card__link {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: var(--fw-semibold, 600);
  color: var(--ss-link, #a98ff8);
  text-decoration: none;
  white-space: nowrap;
}

.today-movers-card__link:hover,
.today-movers-card__link:focus-visible {
  color: var(--ss-link-hover, #c4b5fd);
  text-decoration: underline;
}

.today-mover-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Mobile base: identity, meter, then the numbers. */
.today-mover {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "id    metrics"
    "meter meter";
  align-items: center;
  column-gap: 12px;
  /* The bar belongs to the holding above it, so the gap between the company
     name and the meter stays inside the 8-12px band rather than reading as
     three separate pieces. */
  row-gap: 9px;
  padding: 10px var(--space-md, 12px);
  border-top: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.08));
}

.today-mover-item:first-child .today-mover { border-top: 0; }

/* Where a row opens its holding in Positions it is a real link -- pointer
   cursor, hover lift, keyboard focus ring. No motion beyond the colour change.
   That is the app, and Home, whose rows open the holding in the Demo (C70).
   Other public rows (the free tool's) are not links and get none of this. */
a.today-mover {
  cursor: pointer;
  text-decoration: none;
  color: inherit;
  transition: background-color 0.12s ease;
}

a.today-mover:hover,
a.today-mover:focus-visible {
  background: rgba(255, 255, 255, 0.05);
  text-decoration: none;
  color: inherit;
}

a.today-mover:hover .today-mover__meter-fill,
a.today-mover:focus-visible .today-mover__meter-fill { opacity: 1; }

/* Desktop: the row lifts a little on hover -- background, logo, ticker and
   bar each a step brighter. No movement, no glow. The brightness sits on the
   badge wrapper rather than the image so the image's own drop-shadow is not
   replaced. */
@media (min-width: 768px) {
  a.today-mover .snapshot-logo-badge,
  a.today-mover .snapshot-analytics-title-row .fw-bold,
  a.today-mover .snapshot-company-name {
    transition: filter 0.12s ease, color 0.12s ease, opacity 0.12s ease,
      transform 0.12s ease;
  }

  a.today-mover .snapshot-logo-badge { transform: translateY(0); }
  a.today-mover:hover .snapshot-logo-badge {
    filter: brightness(1.14);
    transform: translateY(-1px);
  }
  a.today-mover:hover .snapshot-analytics-title-row .fw-bold { color: #ffffff; }
  a.today-mover:hover .snapshot-company-name { opacity: 1; }
}

a.today-mover:focus-visible {
  outline: 2px solid var(--ss-focus-ring, rgba(169, 143, 248, 0.6));
  outline-offset: -2px;
}

.today-mover__id {
  grid-area: id;
  min-width: 0;
  /* The shared identity macro's ticker (.fw-bold) takes its size from the
     container. In Positions that container is a table cell at the table's
     14px; a list item would otherwise inherit the page's 16px and render
     the same markup a size larger. */
  font-size: var(--fs-body, 14px);
}

/* The meter. The track is barely there -- it exists so a small contribution
   still reads as "a little of the whole" rather than a stray dash. */
.today-mover__meter {
  grid-area: meter;
  min-width: 0;
  height: 12px;
  border-radius: 999px;
  /* Context only: the coloured bar is what the eye should land on. 0.022
     (1.05:1) vanished on the lifted card (C44); 0.06 shows the track. */
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
}

.today-mover__meter-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  opacity: 0.9;
  transition: opacity 0.12s ease;
}

.today-mover--up .today-mover__meter-fill { background: var(--accent-green, #34d399); }
.today-mover--down .today-mover__meter-fill { background: var(--accent-red, #fb8a8a); }
.today-mover--flat .today-mover__meter-fill { background: transparent; }

/* Dollar impact over percentage as one right-aligned block, so the two read
   as a pair rather than as labels floating on opposite sides of the row. */
.today-mover__metrics {
  grid-area: metrics;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
  min-width: 0;
}

/* Companion metrics: read together, so they share a size and a colour. The
   dollar keeps one weight more, which is all the precedence it needs. */
.today-mover__value {
  font-size: 15px;
  font-weight: var(--fw-bold, 700);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  line-height: 1.25;
  white-space: nowrap;
}

.today-mover--up .today-mover__value { color: var(--accent-green, #34d399); }
.today-mover--down .today-mover__value { color: var(--accent-red, #fb8a8a); }
.today-mover--flat .today-mover__value { color: var(--ss-text-secondary); }

.today-mover__pct {
  font-size: 14px;
  font-weight: var(--fw-semibold, 600);
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* Same hue and family as the dollar, exactly one step back: 14/600 against
     15/700. Companion values, not a value and its footnote. */
  opacity: 0.95;
}

.today-mover--up .today-mover__pct { color: var(--accent-green, #34d399); }
.today-mover--down .today-mover__pct { color: var(--accent-red, #fb8a8a); }
.today-mover--flat .today-mover__pct { color: var(--ss-text-secondary); }

@media (min-width: 768px) {
  .today-mover {
    /* The plot column is capped rather than greedy: a full-width track left a
       long grey remainder on every small mover. */
    /* A fixed identity column, sized to the badge plus a ticker and a short
       name, so the bar starts just past the holding on every row and the bar
       origins still line up. The meter then takes all remaining width and the
       metrics sit at a fixed width against the right edge -- no dead band
       either side of the bar. */
    grid-template-columns: 10.5rem minmax(120px, 1fr) 7rem;
    grid-template-areas: "id meter metrics";
    row-gap: 0;
    padding: var(--space-md, 12px) var(--card-pad-x, 16px);
  }

  .today-mover__meter { height: 11px; }

  .today-hero__spark {
    width: 50%;
    bottom: 14%;
    height: 71%;
    opacity: 1;
  }

  .today-hero__spark-link { display: block; }
}

.today-empty {
  padding: var(--space-lg, 16px) var(--card-pad-x, 16px);
  margin: 0;
  font-size: var(--fs-body, 14px);
  color: var(--ss-text-secondary);
}

@media (max-width: 575.98px) {
  /* Tighter vertically than horizontally: the card's side rails still frame
     the value, but the crown and floor come in so the hero costs less of a
     phone's first screen and more of Today's Movers clears the fold. Scoped
     to the phone -- the desktop card keeps its 16px band.

     The two vertical figures are not symmetric on purpose: the uppercase
     kicker at the crown carries its own leading above the caps, while the
     change figure's line box ends almost at its descenders, so an even 4px
     reads heavier at the floor. 7px at the bottom puts the same optical air
     on both edges. */
  .today-hero { padding: 4px var(--space-md, 12px) 7px; }

  /* The day's change is the value's own second line, not a separate block.
     Zero here rather than a smaller number: the 11px label already carries
     ~5px of half-leading above its caps, so the ink-to-ink band still reads
     as 8px -- tighter than the 10.5px holding "Portfolio Value" to the value
     itself, which is what makes the four lines scan as one unit instead of
     two stacked label/value pairs. */
  .today-hero__change-label { margin-top: 0; margin-bottom: 0; }

  /* The phone dims the whole chart to 0.58 so it cannot compete with the
     value. That flat multiplier costs the line more than it costs the fill,
     which was already near-invisible -- so the line alone swaps to a gradient
     12% stronger at every stop, restoring its confidence without touching the
     stroke width, the fill, or the left-to-right fade. Desktop renders at full
     opacity and keeps the original gradient. */
  .today-hero__spark-line { stroke: url(#todayHeroSparkLineDense); }
  .today-movers-card__note {
    padding: var(--space-sm, 8px) var(--space-md, 12px) var(--space-md, 12px);
  }
}

/* ── Your Investment ─────────────────────────────────────────────────
   A compact answer, not a dashboard: three stat figures, an optional
   proportional bar, and a way through to Performance's historical version
   of the same question. Numbers come from `investment_growth.py` via
   `today_view_model['investment']` -- nothing here computes a dollar
   figure of its own. */
/* The card keeps the standard .pop2-card-head; its body carries the side
   padding, matched to the head's at each width (portfolio.html narrows the
   head to --space-md at 480px and below), so the kicker lines up with every
   other card's and the head's rule runs edge to edge. */
.today-investment-body {
  padding: 0 var(--card-pad-x, 16px) var(--space-lg, 16px);
}

@media (max-width: 480px) {
  .today-investment-body { padding-left: var(--space-md, 12px); padding-right: var(--space-md, 12px); }
}

.today-investment-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-md, 12px);
  margin-top: var(--space-sm, 8px);
}

.today-investment-stat__label {
  margin: 0 0 2px;
  font-size: 11px;
  font-weight: var(--fw-semibold, 600);
  letter-spacing: 0.04em;
  color: var(--ss-text-muted);
}

.today-investment-stat__value {
  margin: 0;
  font-size: clamp(1rem, 4.2vw, 1.2rem);
  font-weight: var(--fw-bold, 700);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--text-primary, #e6edf3);
}

/* Proportional composition. Only ever rendered when the backend has already
   decided contributions and growth are both non-negative shares of current
   value (see `is_meaningful_composition` in investment_growth.py) -- this
   markup never appears for a withdrawal-heavy or loss-heavy portfolio, so it
   never needs to represent a state it cannot draw honestly. */
.investment-composition {
  margin-top: var(--space-md, 12px);
}

.investment-composition-bar {
  display: flex;
  width: 100%;
  height: 9px;
  border-radius: var(--ss-radius-pill, 999px);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
}

.investment-composition-bar__segment { display: block; height: 100%; }

/* Contributions are informational, not a gain -- the app's neutral/theme
   colour, not green. */
.investment-composition-bar__segment--contributions {
  background: var(--ss-brand-light, #a98ff8);
}

.investment-composition-bar__segment--growth {
  background: var(--accent-green, #34d399);
}

.investment-composition-legend {
  display: flex;
  justify-content: space-between;
  gap: var(--space-sm, 8px);
  margin-top: 6px;
  font-size: 11px;
  font-weight: var(--fw-semibold, 600);
  color: var(--ss-text-secondary);
}

.investment-composition-legend__item--contributions::before,
.investment-composition-legend__item--growth::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 4px;
  border-radius: 50%;
  vertical-align: middle;
}

.investment-composition-legend__item--contributions::before {
  background: var(--ss-brand-light, #a98ff8);
}

.investment-composition-legend__item--growth::before {
  background: var(--accent-green, #34d399);
}

.today-investment-cta {
  display: inline-block;
  margin-top: var(--space-md, 12px);
  font-size: 12px;
  font-weight: var(--fw-semibold, 600);
  color: var(--ss-link, #a98ff8);
  text-decoration: none;
}

.today-investment-cta:hover,
.today-investment-cta:focus-visible {
  color: var(--ss-link-hover, #c4b5fd);
  text-decoration: underline;
}

@media (max-width: 575.98px) {
  .today-investment-stats { gap: var(--space-sm, 8px); }
  .today-investment-stat__value { font-size: 1rem; }
}

/* ── Public profile (Home's band) ────────────────────────────────────
   A public page doesn't load portfolio.html's card styles, so the visual
   states the few it uses; public-site.css then takes the card frame and the
   side padding off, so the visual sits on the band. */
.today-snapshot-embed--showcase .pop2-card-row { margin-bottom: 0.85rem; }

.today-snapshot-embed--showcase .pop2-card {
  background: #0c1526;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: none;
}

.today-snapshot-embed--showcase .pop2-card-head { padding: 0.75rem 1rem 0; }

.today-snapshot-embed--showcase .pop2-kicker {
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ss-text-secondary);
  font-weight: 700;
  margin: 0;
}

.today-snapshot-embed--showcase .today-hero { padding: 0.75rem 1rem 1rem; }
.today-snapshot-embed--showcase .today-mover-list { padding: 0 1rem 0.75rem; }

.today-snapshot-caption {
  margin: 0.6rem 0 0;
  text-align: center;
  font-size: 0.78rem;
  color: var(--ss-text-muted);
}
