/*
 * Positions tab -- the holdings table page.
 *
 * Deliberately small. Every part of the table's look (card, row rhythm,
 * logo badge, sector accent bar, tabular figures, green/red) is the
 * Allocation positions table's, reused as-is: `.pop2-card`, `.pop2-table`,
 * `.snapshot-holding-cell`, `.snapshot-logo-badge*`,
 * `.snapshot-analytics-title-row`, `.snapshot-company-name`,
 * `.pop2-shares`, `.snapshot-analytics-number-cell`, `.pop2-pos`/`.pop2-neg`
 * (portfolio.html) and the shared scroll container / scrollbar styling
 * (snapshot-analytics.css, where `.positions-table-scroll` and
 * `.positions-table` are added to the existing selector groups).
 *
 * What is left for this file: the table's own pane and sticky header, the
 * grouping gutters that separate lifetime figures from today's, the sort
 * affordance on the column headers, and the footnote/empty states.
 */

/* ── Scroll container ───────────────────────────────────────────────── */

/*
 * Nine columns cannot collapse to a phone's width, so above 860px this table
 * keeps every column and scrolls horizontally rather than hiding the wider
 * ones the way the Allocation table does. Nothing is hidden behind a hover or
 * a tap. Below 860px the table is not shown at all: the phone gets the list
 * at the foot of this file instead.
 *
 * Horizontal only. There is no `max-height` and no vertical overflow here:
 * the page is the one vertical scrolling context on this screen, and the
 * table grows to whatever its rows need. `overflow-y: hidden` exists to keep
 * a vertical scrollbar from ever appearing on the wrapper -- it clips
 * nothing, because the wrapper is always exactly as tall as its table.
 */
.positions-table-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
}

/* The identity column stays readable while the numbers scroll past it. Two
   lines now (ticker over company name) rather than four, so it needs less
   width than it did when sector and industry sat under them. */
.positions-table .snapshot-analytics-primary-cell {
  min-width: 200px;
}

/* ── Sticky header ──────────────────────────────────────────────────────
 *
 * The table has no height of its own: it grows to fit every row, the page
 * owns vertical scrolling, and the footer lands after the last holding.
 *
 * That leaves the header sticking to the *page*, which the wrapper above
 * would normally prevent -- `overflow-x: auto` makes an element a scroll
 * container on both axes, and sticky then resolves against a wrapper that
 * never scrolls vertically. table-scroll.js resolves it by measurement:
 * while the table fits, the wrapper has nothing to scroll horizontally, so
 * it leaves the scrolling chain (`.is-fitting` below) and the header sticks
 * to the page. Once the table is wider than its wrapper -- narrow windows,
 * and phones -- horizontal swiping matters more than a fixed header, so the
 * wrapper takes `overflow-x` back and the header scrolls with the page.
 */
.positions-table-scroll.is-fitting {
  overflow: visible;
}


/* ── Gain / Day colour ──────────────────────────────────────────────── */

/*
 * Same classes and the same `--accent-green` / `--accent-red` tokens the
 * Performance Metrics cards use (see .pop2-compare-value in
 * performance_tab.html) -- repeated here only to win on specificity.
 * `.pop2-pos` alone is (0,1,0) and loses to `.pop2-table tbody td` (0,2,1)
 * in portfolio.html, which is why the classes have no effect on a table
 * cell without this. Nothing about the colours themselves is new.
 */
.positions-table tbody td.pop2-pos {
  color: var(--accent-green);
  font-weight: var(--fw-bold);
}

.positions-table tbody td.pop2-neg {
  color: var(--accent-red);
  font-weight: var(--fw-bold);
}

/* A genuinely flat move is neither a gain nor a loss -- it takes the muted
   treatment rather than borrowing one of the two signals. */
.positions-table tbody td.pop2-muted {
  color: var(--text-muted);
}

/* ── Column grouping ────────────────────────────────────────────────────
 *
 * Nine columns are four ideas: what the position is (Holding, Weight, Market
 * Value), what it has made since purchase (Total Gain $/%), what it did this
 * session (Day $/%), and the raw facts (Shares, Last Price). Without some
 * grouping, "Total Gain %" and "Day $" read as neighbours of equal standing
 * and the reader has to keep the header row in mind to tell lifetime from
 * today.
 *
 * The grouping is spacing, not chrome: a hairline at each boundary -- lighter
 * than the row rules, so it separates without drawing attention -- a wider
 * gutter after it, and a tighter gutter inside each pair. There is no second
 * header row, no fill, and no change to any cell's contents.
 */

.positions-table th.positions-group-start,
.positions-table td.positions-group-start {
  border-left: 1px solid rgba(146, 160, 181, 0.14);
  padding-left: 26px;
}

/* The second half of a pair sits closer to the first than the pair sits to
   the next group: 18px inside the pair against 38px across the boundary.
   That ratio is what makes the pairs read as pairs without a label. */
.positions-table td.positions-group-tail {
  padding-left: 6px;
}

/* The header cells carry no padding of their own -- the sort button is the
   padded box (see below) -- so the gutters are set on the button. */
.positions-table th.positions-group-start .positions-sort-btn {
  padding-left: 26px;
}

.positions-table th.positions-group-tail .positions-sort-btn {
  padding-left: 6px;
}

/* ── Today over lifetime ────────────────────────────────────────────────
 *
 * The reader came from Today asking which holdings produced the day, so the
 * Day pair is what the eye should land on first. Both pairs keep the same
 * two hues -- no third colour enters the table. The separation is weight and
 * presence: Today at full strength, lifetime one step back. Small on any one
 * cell, unmistakable down a column of twenty-four.
 */
.positions-table tbody td.positions-col-today.pop2-pos,
.positions-table tbody td.positions-col-today.pop2-neg {
  font-weight: var(--fw-bold, 700);
}

.positions-table tbody td.positions-col-lifetime.pop2-pos,
.positions-table tbody td.positions-col-lifetime.pop2-neg {
  font-weight: var(--fw-semibold, 600);
  opacity: 0.78;
}

/* The header row says the same thing one step quieter, so the emphasis is
   already there before the first number is read. */
.positions-table thead th.positions-col-today .positions-sort-btn {
  color: var(--text-primary, #e8f0fb);
}

/* ── Header row ─────────────────────────────────────────────────────────
 *
 * The header sat at the card's own background and the same muted grey as
 * the sector captions below it, so it read as another line of text rather
 * than as the table's controls. It now takes a faint ground of its own and
 * a step up in text contrast -- enough to separate it from the rows, not
 * enough to become a band of chrome.
 */
/*
 * Sticky only where it can actually reach the page.
 *
 * `position: sticky` resolves against the nearest scroll container. While the
 * table fits, the wrapper is `overflow: visible` and that container is the
 * viewport, so the header pins under the navigation -- which is the whole
 * point. Once the table is wider than its wrapper the wrapper takes
 * `overflow-x` back to allow swiping, and it becomes the scrollport itself.
 * A non-zero `top` is then measured from the top of a box that never scrolls
 * vertically, so sticky pushes the header *down* by the offset and holds it
 * there: a permanent empty strip above the column names, with the table's own
 * column rules running through it, and the first row hidden behind the
 * header. It appeared below roughly 1270px, wherever the nine columns stopped
 * fitting.
 *
 * So the sticky declarations live behind `.is-fitting`. In the scrolling
 * state the header is an ordinary header again -- no offset, no gap, nothing
 * covered -- which is also the no-JavaScript default, since nothing adds the
 * class then.
 */
.positions-table-scroll.is-fitting .positions-table thead th {
  position: sticky;
  /* The navigation bar's own measured bottom edge (table-scroll.js),
     falling back to the layout's published height and then to a fixed
     guess. Taking it from the bar itself is what keeps the header
     against it rather than parked below it. */
  top: var(--ss-sticky-offset, var(--ss-topnav-height, 64px));
  /* Above the rows, below the logo badges' own stacking. */
  z-index: 2;
}

.positions-table thead th {
  padding: 0;
  /* Under the fixed top navigation, not behind it. `--ss-topnav-height` is
     measured and published by layout_dashboard.html, so this follows the bar
     when it changes height between breakpoints. */
  /* Opaque, not a translucent tint: the header travels over the rows, and a
     see-through header shows the numbers sliding underneath it. The tint is
     layered on top of the card's own ground instead of replacing it, so the
     colour is the same one a non-sticky header had. */
  background:
    linear-gradient(rgba(255, 255, 255, 0.028), rgba(255, 255, 255, 0.028)),
    var(--bg-card-soft);
  border-bottom: 1px solid rgba(146, 160, 181, 0.26);
  /* It sits over the rows for as long as the page scrolls, so it casts a
     little. Tight and mostly negative-spread: a hairline of depth at the
     boundary, not a band of shade over the first row. */
  box-shadow: 0 6px 10px -8px rgba(0, 0, 0, 0.7);
}


/* ── Sortable headers ───────────────────────────────────────────────────
 *
 * Every column sorts, and that has to be visible before anyone hovers. The
 * indicator is therefore always drawn -- a neutral up/down glyph, quiet but
 * legible, at the size of the label beside it -- rather than appearing only
 * on the active column. The whole cell is the target: the button fills it
 * and lights up under the pointer, so the hit area is felt as well as seen.
 *
 * `.pop2-table thead th` already sets `cursor: pointer` and `user-select:
 * none`; the button inherits the header's uppercase/letter-spacing, so the
 * sortable headers still look like the table's own header row.
 */
.positions-sort-btn {
  width: 100%;
  /* A taller target than the 12px the cells use. The row grows by 4px and
     the header stops being a line of small text to aim at. */
  padding: 14px var(--space-md, 12px);
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  justify-content: flex-end;
  white-space: nowrap;
  gap: 5px;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.positions-table thead th.ps-3 .positions-sort-btn {
  justify-content: flex-start;
}

.positions-sort-btn:hover,
.positions-sort-btn:focus-visible {
  color: var(--text-primary, #e8f0fb);
  background: rgba(255, 255, 255, 0.05);
}

.positions-sort-btn:hover .snapshot-analytics-sort-indicator,
.positions-sort-btn:focus-visible .snapshot-analytics-sort-indicator {
  opacity: 1;
}

.positions-sort-btn:focus-visible {
  outline: 2px solid var(--ss-focus-ring);
  outline-offset: -2px;
}

/* Indicator: always present, so no column can look unsortable. A neutral
   up/down glyph at 13px reads as a control at a glance; the earlier 12px at
   0.35 opacity read as punctuation. */
.positions-table thead th .snapshot-analytics-sort-indicator {
  font-size: 13px;
  line-height: 1;
  opacity: 0.6;
  transition: opacity 0.12s ease, color 0.12s ease;
}

.positions-table thead th .snapshot-analytics-sort-indicator::after {
  content: "\2195";
}

/* Active column: the glyph resolves to the direction actually applied and
   goes full strength, the label brightens, and the cell takes a ground and
   a bar along its foot. Three quiet signals together, so the active sort is
   obvious without any one of them being loud. */
.positions-table thead th[aria-sort="ascending"] .snapshot-analytics-sort-indicator::after {
  content: "\2191";
}

.positions-table thead th[aria-sort="descending"] .snapshot-analytics-sort-indicator::after {
  content: "\2193";
}

.positions-table thead th[aria-sort="ascending"] .snapshot-analytics-sort-indicator,
.positions-table thead th[aria-sort="descending"] .snapshot-analytics-sort-indicator {
  opacity: 1;
  color: var(--text-primary, #e8f0fb);
}

.positions-table thead th[aria-sort="ascending"],
.positions-table thead th[aria-sort="descending"] {
  background:
    linear-gradient(rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.07)),
    var(--bg-card-soft);
  /* Keeps the header's own shadow: `box-shadow` replaces rather than adds. */
  box-shadow:
    inset 0 -2px 0 rgba(210, 223, 239, 0.5),
    0 6px 10px -8px rgba(0, 0, 0, 0.7);
}

.positions-table thead th[aria-sort="ascending"] .positions-sort-btn,
.positions-table thead th[aria-sort="descending"] .positions-sort-btn {
  color: var(--text-primary, #e8f0fb);
}

/* ── Rows ───────────────────────────────────────────────────────────────
 *
 * `.pop2-table` already gives a row with `data-row-id` a pointer cursor and
 * a hover ground; this lifts that a little and brings the ticker up with it,
 * so a row reads as one object under the pointer rather than as nine cells
 * that happened to change colour together.
 */
.positions-table tbody tr[data-row-id] td {
  transition: background-color 0.12s ease;
}

.positions-table tbody tr[data-row-id]:hover td {
  background: rgba(255, 255, 255, 0.055);
}

/* The ticker brightens with the row, the way a mover row does on Today.
   There is no sector accent bar here to bring up with it -- sector is
   Allocation's, and the identity cell is logo, ticker and name only. */
.positions-table tbody tr[data-row-id] .snapshot-analytics-title-row .fw-bold {
  transition: color 0.12s ease;
}

.positions-table tbody tr[data-row-id]:hover .snapshot-analytics-title-row .fw-bold {
  color: #ffffff;
}

/* ── Footnotes / empty state ────────────────────────────────────────── */

.positions-note {
  margin: 10px var(--card-pad-x, 16px) 0;
  color: var(--text-muted, #92a2b8);
  font-size: 0.74rem;
}

.positions-note:last-child {
  margin-bottom: 10px;
}

.positions-empty {
  margin: 0;
  padding: var(--card-pad-y, 16px) var(--card-pad-x, 16px);
  color: var(--text-muted, #92a2b8);
  font-size: 0.85rem;
}


/* ══ The phone list ═════════════════════════════════════════════════════
 *
 * Below 860px the table is replaced rather than reflowed.
 *
 * The two are different tools. The table exists to compare nine columns
 * across twenty-four holdings at once, which is a thing a desktop is for and
 * a phone is not: every attempt to keep all nine on a phone ends up either
 * scrolling sideways or shrinking type until the figures need a squint. The
 * list answers one question at a time instead -- the "Showing" control names
 * which -- and gives that question's figure the whole of the row's emphasis.
 *
 * Both are rendered from the same view-model rows in the same server order.
 * The table is untouched by everything below; it is simply not displayed
 * here, and the list is not displayed above the breakpoint.
 */

/* Desktop default: the list does not exist. Declared outside the media query
   so no desktop rule has to switch it off. */
.positions-mobile-shell {
  display: none;
}

@media (max-width: 860px) {
  .positions-mobile-shell {
    display: block;
  }

  /* The table and its horizontal scrolling both go. */
  .positions-table-scroll {
    display: none;
  }

  /* ── "Showing": the control that picks the question ──
   *
   * A segmented control rather than a dropdown: four options is few enough to
   * show at once, and the one that is on is then a fact on screen rather than
   * something to open a menu to check.
   */
  /* Just the track now. The four segments carry their own meaning and one of
     them is lit, so a caption over them only added a line of chrome. */
  .pm-modes {
    padding: var(--space-md, 12px) var(--card-pad-x, 16px);
    border-bottom: 1px solid var(--border-subtle);
  }

  .pm-notices {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: var(--space-md, 12px) var(--card-pad-x, 16px) 0;
  }

  .pm-modes__track {
    display: flex;
    gap: 2px;
    padding: 2px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
  }

  /* `flex: 1 1 0` off a zero basis, with `min-width: 0` so a long word cannot
     claim more than its share: four equal quarters at every width, whatever
     the labels say. The segments keep the one track and its one ground -- they
     are a single control, not four pills. */
  .pm-mode {
    display: flex;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* One height for all four, so the single-line label sits on the same
       centre line as the two-line ones instead of riding higher. */
    min-height: 50px;
    padding: 6px 3px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 0.72rem;
    font-weight: var(--fw-semibold, 600);
    text-align: center;
    cursor: pointer;
    transition: background-color 0.14s ease, color 0.14s ease;
  }

  /* One word to a line, and never broken across one: the line breaks are in
     the markup, so the only thing left to forbid is the browser adding its
     own inside a word. */
  .pm-mode__line {
    display: block;
    white-space: nowrap;
    line-height: 1.2;
  }

  .pm-mode:hover {
    color: var(--text-primary);
  }

  .pm-mode.is-active {
    background: var(--ss-active-fill);
    color: #ffffff;
    /* Inset, not a border: the track's buttons keep their exact size. */
    box-shadow: inset 0 0 0 1px var(--ss-active-line);
    /* A step up from the 600 the others carry -- enough to feel chosen rather
       than merely tinted, without changing the control's colours. */
    font-weight: var(--fw-bold, 700);
  }

  /* The control is the sort control, and only the active segment is sorting.
     The glyph is the table's own descending arrow (U+2193, the same one
     `.snapshot-analytics-sort-indicator` resolves to for `aria-sort="descending"`),
     not a caret -- this opens nothing, it says which way the list is ordered.
     It rides on the label's last line so it reads as part of the phrase. */
  .pm-mode.is-active .pm-mode__line:last-child::after {
    content: "\2193";
    margin-left: 3px;
    font-weight: var(--fw-semibold, 600);
    opacity: 0.85;
  }

  .pm-mode:focus-visible {
    outline: 2px solid var(--ss-focus-ring);
    outline-offset: -2px;
  }

  /* ── The list ── */
  .positions-mobile {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* One holding. Three lines of text against a logo, and the figures set
     right -- the row's whole job is that the eye lands on one number. */
  .pm-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-md, 12px);
    padding: 10px var(--card-pad-x, 16px);
    border-bottom: 1px solid var(--border-subtle);
    cursor: pointer;
    transition: background-color 0.12s ease;
  }

  .pm-row:last-child {
    border-bottom: 0;
  }

  .pm-row:hover {
    background: rgba(255, 255, 255, 0.04);
  }

  .pm-logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    /* The logo sits against the ticker, not the middle of the row: the third
       line is metadata and should not drag the badge down past it. */
    margin-top: 1px;
  }

  .pm-logo__img,
  .pm-logo__fallback {
    width: 34px;
    height: 34px;
    border-radius: 50%;
  }

  /* Same badge treatment the table's logos use, at the list's size. */
  .pm-logo__img {
    object-fit: contain;
    padding: 3px;
    background: rgba(11, 23, 44, 0.95);
    border: 1px solid rgba(144, 167, 200, 0.55);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.30);
  }

  .pm-logo__fallback {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #dbe7f6;
    background: rgba(100, 142, 207, 0.24);
    border: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 13px;
    font-weight: var(--fw-bold, 700);
    letter-spacing: 0.03em;
  }

  .pm-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Identity on the left, the mode's two figures on the right. */
  .pm-head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-md, 12px);
    min-width: 0;
  }

  .pm-id {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Top of the hierarchy, with the mode's primary figure. */
  .pm-ticker {
    color: var(--text-primary);
    font-size: 0.95rem;
    font-weight: var(--fw-bold, 700);
    line-height: 1.25;
    letter-spacing: 0.01em;
  }

  .pm-name {
    color: var(--text-secondary);
    font-size: 0.72rem;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .pm-figs {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    align-items: flex-end;
    text-align: right;
  }

  .pm-primary {
    font-size: 0.95rem;
    font-weight: var(--fw-bold, 700);
    line-height: 1.25;
    white-space: nowrap;
    color: var(--text-primary);
  }

  .pm-secondary {
    font-size: 0.72rem;
    font-weight: var(--fw-semibold, 600);
    line-height: 1.3;
    white-space: nowrap;
    color: var(--text-secondary);
  }

  /* Signed figures keep the page's own green and red. A flat move is neither,
     so it takes the muted treatment rather than borrowing one of the two. */
  .pm-primary.pop2-pos,
  .pm-secondary.pop2-pos {
    color: var(--accent-green);
  }

  .pm-primary.pop2-neg,
  .pm-secondary.pop2-neg {
    color: var(--accent-red);
  }

  .pm-primary.pop2-muted,
  .pm-secondary.pop2-muted {
    color: var(--text-muted);
  }

  /* ── Metadata ──
   *
   * The bottom of the hierarchy: present, never competing. No labels -- the
   * shape of "4.1% • $11.8K" is read once and then recognised, and repeating
   * "Weight" and "Market Value" down twenty-four rows is noise that says the
   * same thing every time.
   */
  .pm-meta {
    margin-top: 2px;
    color: var(--text-muted);
    /* The 12px type floor (C30 decision 4). */
    font-size: 0.75rem;
    font-weight: var(--fw-regular, 400);
    line-height: 1.3;
    white-space: nowrap;
    /* Already the muted token; the step down from the company name beside it
       is contrast rather than size, so it stays legible while still reading
       as the last line. */
    opacity: 0.9;
  }

  /* 0.75 of the line's 0.9: the dot lands at ~0.68, over the 0.65 floor
     for text. */
  .pm-meta__dot {
    margin: 0 5px;
    opacity: 0.75;
  }

  /* In the two modes whose metadata is the session move, the figure keeps its
     hue -- up or down is worth knowing at a glance -- but stands a step back,
     the same step the table's lifetime columns take against today's. At full
     strength it pulled the eye off the primary figure above it, which is the
     one the mode was chosen to answer. 0.85 of the line's 0.9 (~0.77) keeps
     the red at 5:1 on the card; at 0.72 it read 3.9:1, under 4.5. */
  .pm-meta .pop2-pos {
    color: var(--accent-green);
    opacity: 0.85;
  }

  .pm-meta .pop2-neg {
    color: var(--accent-red);
    opacity: 0.85;
  }

  .pm-meta .pop2-muted {
    color: var(--text-muted);
  }

  /* ── Mode switching ──
   *
   * Every mode's figures are in the DOM; the mode shows its own and hides the
   * rest. A figure declares the modes it belongs to in `data-pm-for`, so the
   * metadata line can belong to two of them without being written twice.
   */
  .positions-mobile[data-mode="day"] [data-pm-for]:not([data-pm-for~="day"]),
  .positions-mobile[data-mode="overall"] [data-pm-for]:not([data-pm-for~="overall"]),
  .positions-mobile[data-mode="weight"] [data-pm-for]:not([data-pm-for~="weight"]),
  .positions-mobile[data-mode="value"] [data-pm-for]:not([data-pm-for~="value"]) {
    display: none;
  }

  /* The footnotes belong to the list now. */
  .positions-note {
    margin-left: var(--card-pad-x, 16px);
    margin-right: var(--card-pad-x, 16px);
  }
}

/* The narrowest phones. Only the type comes down -- the row keeps its shape,
   because there is nothing on it that can be dropped. */
@media (max-width: 359px) {
  .pm-ticker,
  .pm-primary {
    font-size: 0.88rem;
  }

  .pm-name,
  .pm-secondary {
    font-size: 0.69rem;
  }

  .pm-mode {
    font-size: 0.71rem;
    padding: 6px 1px;
  }
}
