repos

Mobile polish: home Picks stacks 1-up; Industries sector table reflows

8d173f13 by Isaac Bythewood · 1 month ago

Mobile polish: home Picks stacks 1-up; Industries sector table reflows

Home Top picks panel was a 2-up grid at every width — at 390px each card's
right column clipped the score figure. Drop to a single-column grid below
$bp-sm, reinstate the 2-up subgrid alignment from $bp-sm up, and let the
row carry a `pick__name` slot on phone (hidden again at $bp-sm where the
narrower card has no room for it).

/industries sector index used an 8-column table that overflowed at phone
widths. Add a `--sectors` modifier so only the index table reflows
(detail-page tables stay native), hide thead, lay each row out as a
5-column card: sector header on row 1, "N members · N industries" caption
on row 2, today / 5d / 1m / 3m / 1y percentages with eyebrow labels on
row 3. Desktop is untouched.
modified frontend/static_src/home/styles/home.scss
@@ -335,27 +335,20 @@   above the fold; on desktop they spread out into one row. */.picks-grid {  display: grid;  grid-template-columns: repeat(2, minmax(0, 1fr));  /* Single column at the narrowest widths: a 2-up grid clipped the score     column on a 390px phone, and stacking buys back room for the company     name. 2-up reinstated at $bp-sm where each card has ~280px to work with. */  grid-template-columns: minmax(0, 1fr);  gap: 12px;}/* Each card is a subgrid of the picks-grid, spanning three shared row   tracks (title, desc, list). That makes the desc row tall enough to fit   whichever horizon's description wraps deepest, so the list itself starts   at the same Y across all cards in the row. Without this the per-card   intrinsic heights drift and the rank-1 rows wouldn't line up. *//* Mobile baseline: a plain block card. Each card stacks vertically and   its own children flow normally so the title/desc/list sequence reads   top-to-bottom. The subgrid alignment kicks in at $bp-sm once we're   back to a multi-column layout where lining rows up matters. */.picks-col {  @include card;  padding: 10px 12px 8px;  display: grid;  /* columns subgrid so the card's single inner column matches its 1fr of     the parent grid (without it, the inner column auto-sized to the desc's     max-content and overflowed the card). Rows subgrid so the title, desc,     and list rows all line up across the four cards. */  grid-template-columns: subgrid;  grid-template-rows: subgrid;  grid-row: span 3;  row-gap: 4px;  min-width: 0;}
@@ -397,14 +390,13 @@  border-bottom: 0;}/* The pick row sheds the name column entirely — at desktop the 4-up   layout makes a useful name slice impossible (a single-character "M…"   reads as noise), and at smaller widths the score figure is the   primary read. The four tracks are pinned at fixed widths so rank-1   / rank-2 / ... line up cell-for-cell across the four horizon cards. *//* Phone baseline: the card is full-width, so the row carries the company   name in its own slot. At $bp-sm and up the row drops the name (no longer   enough horizontal room once the grid goes 2-up / 4-up) and shifts the   verdict badge into the flexible track instead. */.pick__row {  display: grid;  grid-template-columns: 1.5ch minmax(4.5ch, auto) 1fr minmax(5.6ch, auto);  grid-template-columns: 1.5ch minmax(4.5ch, auto) minmax(0, 1fr) auto minmax(5.6ch, auto);  align-items: center;  gap: var(--sp-2);  padding: 9px 2px;
@@ -431,11 +423,16 @@  font-size: var(--fs-sm);}/* The name span exists in the template across all breakpoints but never   shows: at every width either the score fits in this slot or the badge   does. Reclaim the column for the badge. *//* The name span shows on phones, where the stacked 1-up card has room for   it, and is hidden once the grid steps up to 2-up and 4-up below   (the slot becomes too narrow for a useful name slice). */.pick__name {  display: none;  color: var(--ink-faint);  font-size: var(--fs-xs);  min-width: 0;  white-space: nowrap;  overflow: hidden;  text-overflow: ellipsis;}/* center the verdict pill in its slot so Strong / Fair / Weak align
@@ -453,6 +450,31 @@  text-align: right;}/* ---------- $bp-sm: picks step up to 2-up; name yields to the badge ---------- */@media (min-width: $bp-sm) {  .picks-grid {    grid-template-columns: repeat(2, minmax(0, 1fr));  }  /* Reinstate the cross-card row alignment now that there's more than one     column to align across. */  .picks-col {    display: grid;    grid-template-columns: subgrid;    grid-template-rows: subgrid;    grid-row: span 3;    row-gap: 4px;  }  .pick__row {    grid-template-columns: 1.5ch minmax(4.5ch, auto) 1fr minmax(5.6ch, auto);  }  .pick__name {    display: none;  }}/* ---------- desktop layering ---------- */@media (min-width: $bp-md) {  .movers {
modified frontend/static_src/industries/styles/industries.scss
@@ -81,6 +81,93 @@  }}/* Phone: the 8-column sector index table is too wide for a 390px viewport   — relayout each row as a 5-column card. Sector header sits on row 1, a   quiet "<n> members · <n> industries" line on row 2, and the five   horizon percentages with their own eyebrow label on row 3. nth-child   selectors key off the template's column order: sec | d1 | d5 | d21 |   d63 | d252 | members | industries. Scoped to `--sectors` so the   3-column "industries inside this sector" table on the detail page   keeps its native table layout (which fits on a phone fine). */@media (max-width: $bp-sm) {  .ind-table--sectors {    display: block;    border-collapse: separate;    margin-bottom: var(--sp-4);    thead { display: none; }    tbody { display: block; }    tr {      display: grid;      grid-template-columns: repeat(5, 1fr);      column-gap: 8px;      row-gap: 4px;      padding: 10px 12px;      margin-bottom: 8px;      background: var(--surface);      border: 1px solid var(--rule);      border-radius: var(--radius);    }    th.ind-table__sec,    td.ind-table__sec {      grid-column: 1 / -1;      padding: 0;      border: none;      font-weight: 500;      font-size: var(--fs-md);    }    td.num {      border: none;      padding: 4px 0 0;      text-align: left;      font-size: var(--fs-xs);      min-width: 0;      &::before {        display: block;        font-family: var(--font-sans);        font-size: 10px;        color: var(--ink-faint);        letter-spacing: 0.08em;        font-weight: 700;        text-transform: uppercase;        margin-bottom: 1px;      }    }    td:nth-child(2)::before { content: "Today"; }    td:nth-child(3)::before { content: "5d"; }    td:nth-child(4)::before { content: "1m"; }    td:nth-child(5)::before { content: "3m"; }    td:nth-child(6)::before { content: "1y"; }    /* Members + Industries: a quiet caption pair on row 2, left + right */    td:nth-child(7),    td:nth-child(8) {      font-family: var(--font-mono);      font-size: 11px;      color: var(--ink-faint);      border: none;      padding: 0;      grid-row: 2;      margin-bottom: 4px;    }    td:nth-child(7) {      grid-column: 1 / span 3;      text-align: left;      &::after { content: " members"; font-family: var(--font-sans); }    }    td:nth-child(8) {      grid-column: 4 / -1;      text-align: right;      &::after { content: " industries"; font-family: var(--font-sans); }    }  }}/* stat row on the detail pages */.ind-stats {  display: grid;
modified templates/pages/industries_index.html
@@ -33,7 +33,7 @@     Each row&rsquo;s composite is an equal-weight average of its members&rsquo; returns.     Click a sector for a deeper read, the seasonality, and the members.</p>  <table class="ind-table">  <table class="ind-table ind-table--sectors">    <thead>      <tr>        <th scope="col" class="ind-table__sec">Sector</th>