/* ——— Signed-in homepage workspace ———
   Rendered only for logged-in visitors, via the {% if user_logged_in %} block
   in home.html. Every rule is scoped under .home-dash so it cannot reach the
   guest homepage, the scan funnel or the repertoire videos below it.
   Class names here must stay in step with that markup. */

/* ——— Widening the frame the two boxes live in ———
   This is where the workspace's width is actually decided, and it used to be
   decided somewhere it could not take effect.

   `.home-dash` carried `width: min(2400px, calc(100% - 24px))`, but a child
   can never be wider than the box that holds it, and its box is
   `.page-shell` — capped at 1560px by `.home-page .page-shell` in
   style_dark.css. So the 2400px figure was dead code: the workspace rendered
   at 1560px, ~772px per panel, however high the child asked to go. That is
   also why the comment on that rule read as though the panels were already
   1190px each — they never were, and the dashboard's six-up metric row and
   the importer's form row were being squeezed into three-up and stacked
   because of it.

   So the cap moves UP to the shell, where it can bind, and `.home-dash`
   becomes a plain `width: 100%` of it. 2400px is kept as the ceiling: at that
   measure each panel is ~1190px, which is what those two rows want, and the
   cap still stops an ultrawide from pulling the two panels apart.

   Scoped to `.home-workspace` rather than `.home-page`, because the guest
   marketing page renders from this same template at the same URL and its
   1560px measure is load-bearing — the hero, the tab strip and the guest
   importer are all built to it, and test_home_tabs.py pins it.

   Above 1100px only. Below that the two panels stack (see the 1100px query),
   so there is nothing to spread and the narrower frame is the better one;
   below 760px `.page-shell` is narrowed again by the phone rules in
   style_dark.css, and this must not undo them. */
@media (min-width: 1101px) {
  body.home-workspace .page-shell {
    width: min(2400px, calc(100% - 32px));
  }
}

/* The header and the footer are the same frame's furniture, and they were
   still at the 1240px base measure. Widening only the content between them
   would leave the logo and the theme switch marooned in the middle of a
   2400px page, so they are widened to the same ceiling and stop agreeing with
   the content. */
@media (min-width: 1101px) {
  body.home-workspace .site-header-inner,
  body.home-workspace .footer-inner {
    width: min(2400px, calc(100% - 32px));
  }
}

.home-dash {
  display: block;
  /* Safe whether .home-view is a block or a grid: spans the full row either way. */
  grid-column: 1 / -1;
  /* The frame's full width, not a width of its own: the cap lives on
     .page-shell above, where it can actually bind. */
  width: 100%;
  /* .home-view > * forces 40px vertical padding; override it for our own rhythm. */
  padding: 0;
  margin: 12px 0 0;

  /* ——— The workspace fits the window ———
     The workspace is the only thing on this view (every marketing section is
     guest-only), so its whole height is a fixed budget rather than a growing
     document: the site header (~52px), .page-shell's own 16/48px padding, and
     a small margin. `100dvh` rather than `100vh` because a desktop browser's
     chrome changes the visible height as the user scrolls, and `vh` would
     size the shell to the window with the toolbars hidden.

     `max-height`, not `height`. A short window or a long opening list must be
     able to push the shell taller rather than have its content clipped — the
     alternative is silently cutting off the bottom of the dashboard. The
     internal scroll regions below are what keep the *normal* case at or
     under this cap, so the cap is a backstop, not the mechanism.

     88px, not the 150px this was. Measured honestly the furniture outside the
     shell is ~52 + 64 + 12 = 128px, so 150px left ~22px of slack on top of
     that — a margin for a footer the workspace view barely shows. Taking the
     slack, and 40px more, is what pushes both panels further down the window.
     The footer lands below the fold instead of just above it, which is the
     right trade on a view whose only content is the workspace: one short
     strip of links, bought with room the importer and the dashboard use on
     every render. */
  --dash-chrome: 88px;
  max-height: calc(100dvh - var(--dash-chrome));
}

/* Below this the cap stops being reachable in practice: the shell would be
   shorter than its own content and the whole workspace would scroll. On a
   small screen, scrolling the page is the correct behaviour, so the cap is
   lifted and the panels go back to growing naturally. */
@media (max-height: 620px), (max-width: 1100px) {
  .home-dash {
    max-height: none;
  }
}

/* ——— The workspace is four tabs ———
   One panel at a time, switched from the rail (`.home-dash-tabs`). That is
   the whole point of the redesign: each piece — welcome, import, practice,
   trends — gets the FULL height of the workspace instead of sharing it with
   the others, so none of them has to compact itself to fit beside a peer.

   Progressive enhancement, gated on the `home-js` marker that home.html's
   inline script sets before first paint:

     · Without JS the rail is hidden (its buttons would be dead controls) and
       every panel renders stacked, so no-JS visitors still reach every piece
       and the page scrolls naturally.
     · With JS the rail appears and exactly one panel is visible; the shell
       keeps its viewport cap and the active panel scrolls internally.

   Below 1100px a single panel is still full-width, so the tabbed mode is
   kept but the rail moves above the panels as a horizontal strip (see the
   media query further down).

   The shell itself is unframed -- see below. */
.home-dash-inner {
  position: relative;
  /* No border or surface of its own. The panels carry the surfaces; a framed
     shell around framed panels is a box inside a box inside a box — the outer
     one adds nothing and reads as a third region. */
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: stretch;
  gap: 14px;
  min-height: 0;
}

/* The rail is hidden by default so a JS-less page never shows dead buttons. */
.home-dash-tabs {
  display: none;
}

/* The panels column and its members. Block by default (stacked); the grid
   form under JS stretches the active panel to its track, which is what lets
   the panel's `max-height` and internal scroll bind. */
.home-dash-panels {
  display: block;
  min-width: 0;
  min-height: 0;
}

.home-dash-panel {
  min-width: 0;
  min-height: 0;
}

/* ——— JS is on: the tabbed workspace ———
   `auto 1fr`: the rail on the left, the visible panel stretching across the
   rest. The panels column is a one-track grid of its own so the active panel
   is a STRETCHED grid item with a definite height — without that, its
   `max-height: 100%` has nothing to bind to and the internal scroll below
   silently becomes an external one. */
html.home-js .home-dash-inner {
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "tabs panels";
  gap: 16px;
}

html.home-js .home-dash-tabs {
  grid-area: tabs;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-self: start;
  min-width: 0;
}

html.home-js .home-dash-panels {
  grid-area: panels;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: stretch;
}

html.home-js .home-dash-panel[data-workspace-panel] {
  display: none;
}

/* The active panel fills the workspace height and scrolls inside the shell.
   A flex column so the inner box (`.home-dash-col` / `.home-practice-dash`)
   stretches to the same height rather than ending in a straight line part
   way down the panel.

   The cap is the SAME viewport-based number as the shell's, but as the
   panel's own definite maximum. `max-height: 100%` resolved against the grid
   area, which was sized by the panel's own content -- a circular measure
   that never bound, so a tall scan pushed the panel taller and the PAGE
   scrolled. A viewport cap binds unconditionally: the panel becomes the
   scroll container, and its flex children (`min-height: 0`) shrink to fit
   it. */
html.home-js .home-dash-panel.is-active {
  display: flex;
  flex-direction: column;
  max-height: calc(100dvh - var(--dash-chrome));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

html.home-js .home-dash-panel.is-active > * {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}

/* ——— Tab rail controls ———
   Icon-and-label buttons in the same machined language as the shortcut cards:
   a bordered pill, a well for the glyph, gold when active. They are only ever
   visible under `html.home-js` (the rail is `display: none` otherwise), so
   these rules do not need the home-js prefix. */
.home-dash-tab {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 3%, transparent), transparent 60%),
    color-mix(in srgb, var(--panel) 92%, var(--bg-2));
  color: var(--muted, rgba(246, 241, 230, 0.68));
  font: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-align: left;
  cursor: pointer;
  transition:
    border-color 0.22s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.22s ease,
    color 0.22s ease,
    box-shadow 0.22s ease;
}

.home-dash-tab:hover,
.home-dash-tab:focus-visible {
  border-color: color-mix(in srgb, var(--gold) 42%, var(--line-strong));
  color: var(--cream, #f6f1e6);
}

.home-dash-tab.is-active {
  border-color: color-mix(in srgb, var(--gold) 44%, var(--line-strong));
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--gold) 13%, transparent), color-mix(in srgb, var(--gold) 3%, transparent)),
    color-mix(in srgb, var(--panel) 90%, var(--bg-2));
  color: var(--cream, #f6f1e6);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 8%, transparent);
}

.home-dash-tab:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--gold) 72%, transparent);
  outline-offset: 2px;
}

.home-dash-tab-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border: 1px solid color-mix(in srgb, var(--gold) 20%, var(--line));
  border-radius: 9px;
  background: color-mix(in srgb, var(--gold) 9%, transparent);
  color: var(--gold);
  transition: border-color 0.22s ease, background 0.22s ease;
}

.home-dash-tab:hover .home-dash-tab-icon,
.home-dash-tab:focus-visible .home-dash-tab-icon,
.home-dash-tab.is-active .home-dash-tab-icon {
  border-color: color-mix(in srgb, var(--gold) 44%, var(--line));
  background: color-mix(in srgb, var(--gold) 15%, transparent);
}

.home-dash-tab-icon svg {
  display: block;
  width: 17px;
  height: 17px;
}

/* The left-hand column: header, shortcuts, importer — one box.

   Given the same machined surface as the dashboard beside it, so the page
   reads as TWO panels rather than as three loose regions: previously the
   importer sat in the bare middle of the shell with no surface of its own,
   which is what made it look like a third column rather than part of the
   left one. */
.home-dash .home-dash-col {
  display: flex;
  flex-direction: column;
  /* A grid item's automatic minimum size is its content's, so without this
     the importer's long unbroken note text would refuse to narrow and push
     the whole column past the 1fr it was given. */
  min-width: 0;
  min-height: 0;
  padding: 20px 22px 22px;
  border: 1px solid var(--line-strong);
  border-radius: 20px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 4%, transparent), transparent 200px),
    color-mix(in srgb, var(--panel) 92%, var(--bg-2));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 7%, transparent);
}

/* ——— One-row header ———
   Four stacked blocks — eyebrow, 2.75rem title, two lines of sub, link —
   came to roughly 200px, which is a quarter of the budget before any content
   appears. Laid out as a row instead, the eyebrow and title sit on the left
   and the copy and the escape-hatch link share the right, and the whole
   header costs about a third of that. The sub keeps its measure via
   `max-width: 58ch` so it does not stretch into a thin ribbon. */
.home-dash-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: 26px;
  row-gap: 4px;
  margin: 0;
  flex: 0 0 auto;
}

.home-dash-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  grid-column: 1;
  margin: 0;
  padding: 4px 10px 4px 8px;
  border: 1px solid color-mix(in srgb, var(--gold) 26%, var(--line));
  border-radius: 999px;
  background: color-mix(in srgb, var(--gold) 8%, transparent);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-bright);
  font-weight: 800;
  white-space: nowrap;
}

/* A live-looking status pip, the kind a high-end console puts on its header. */
.home-dash-eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold-bright);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold-bright) 18%, transparent);
  animation: home-dash-pip 2.8s ease-in-out infinite;
}

@keyframes home-dash-pip {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold-bright) 18%, transparent); }
  50% { opacity: 0.6; box-shadow: 0 0 0 5px color-mix(in srgb, var(--gold-bright) 8%, transparent); }
}

.home-dash-title {
  grid-column: 1;
  margin: 0;
  font-family: var(--serif, Georgia, "Times New Roman", serif);
  font-size: clamp(2rem, 3vw, 2.6rem);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--cream, #f6f1e6);
  background: linear-gradient(180deg, var(--cream) 30%, color-mix(in srgb, var(--cream) 74%, transparent));
  -webkit-background-clip: text;
  background-clip: text;
  white-space: nowrap;
}

.home-dash-sub {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  margin: 0;
  max-width: 64ch;
  font-size: 1.04rem;
  color: var(--muted, rgba(246, 241, 230, 0.68));
  line-height: 1.55;
}

/* ——— Sidebar-aligned feature shortcuts ——— */

/* Five columns, two rows — NOT the ten-across row this was before the
   workspace became two columns.

   Ten across was sized for the full 1400px shell (~130px per card). The
   shortcuts now live in the left column, roughly 660px, so ten across would
   give each card ~60px: every title wraps to two or three lines, the icons
   collide with the text, and the block grows taller than the single row it
   replaced — which is the opposite of what it was for.

   Five across gives ~125px per card, the same measure the ten-across row
   had, so each card is exactly as legible as it was at full width.

   Widened to SIX alongside the frame. At the new ~1190px panel, five across
   gives ~230px per card — more than twice the measure the card was drawn for,
   so each one is mostly empty space with a two-line caption marooned in it.
   Six brings it back to ~190px. Seven would be tighter still, but the cards
   are the second thing on the page and the importer below them is the first,
   so the card row is not allowed to grow past two rows. */
.home-dash-actions {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
  margin-top: 20px;
  flex: 0 0 auto;
}

.home-dash-card {
  position: relative;
  /* A wrapping ROW, not the column it was: the icon and the title share the
     first line and the description takes a second. Stacked, three children
     meant three lines, and ten cards in one row is only affordable at two. */
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  gap: 7px 9px;
  min-width: 0;
  padding: 15px 14px 16px;
  border: 1px solid var(--line-strong);
  border-radius: 16px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 5%, transparent), transparent 60%),
    color-mix(in srgb, var(--panel) 90%, var(--bg-2));
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 6%, transparent);
  transition:
    border-color 0.22s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}

/* A raking sheen that only appears on hover, so the resting state stays calm.
   Its origin is driven by --mx/--my, which the script at the foot of this file
   sets from the pointer; with no JS the default centre still reads correctly. */
.home-dash-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    180px circle at var(--mx, 50%) var(--my, 0%),
    color-mix(in srgb, var(--cream) 9%, transparent),
    transparent 70%
  );
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
}

.home-dash-card:hover,
.home-dash-card:focus-visible {
  border-color: color-mix(in srgb, var(--gold) 46%, var(--line-strong));
  transform: translateY(-3px);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 9%, transparent),
    0 10px 26px -12px rgba(0, 0, 0, 0.6);
}

.home-dash-card:hover::after,
.home-dash-card:focus-visible::after {
  opacity: 1;
}

/* A visible keyboard focus ring. These are real links, and the default ring is
   easy to lose against a raised surface. */
.home-dash-card:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--gold) 72%, transparent);
  outline-offset: 3px;
}

.home-dash-card--primary {
  border-color: color-mix(in srgb, var(--gold) 44%, var(--line-strong));
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--gold) 13%, transparent), color-mix(in srgb, var(--gold) 3%, transparent)),
    color-mix(in srgb, var(--panel) 90%, var(--bg-2));
}

/* The icon sits in a machined well so the row of shortcuts reads as a
   toolbar of controls rather than a wall of loose glyphs.

   It is placed BESIDE the title rather than above it. Stacked, the icon
   costs a full 38px row in every one of ten cards; inline, the card's height
   is set by the title alone and the ten-across row fits in one band. */
.home-dash-card-icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border: 1px solid color-mix(in srgb, var(--gold) 20%, var(--line));
  border-radius: 11px;
  background: color-mix(in srgb, var(--gold) 9%, transparent);
  color: var(--gold);
  transition: border-color 0.22s ease, background 0.22s ease;
}

.home-dash-card:hover .home-dash-card-icon,
.home-dash-card:focus-visible .home-dash-card-icon {
  border-color: color-mix(in srgb, var(--gold) 44%, var(--line));
  background: color-mix(in srgb, var(--gold) 15%, transparent);
}

.home-dash-card-icon svg {
  display: block;
  width: 21px;
  height: 21px;
}

/* Icon and title share the first line, description wraps beneath.

   The card is a wrapping row rather than the column it was: `flex: 1 1 100%`
   on the text pushes it onto its own line while the icon (fixed 30px) and the
   title (flexible) sit side by side above. That is what keeps a ten-across
   card to two short lines instead of three full-height ones. */
.home-dash-card-title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--serif, Georgia, "Times New Roman", serif);
  font-size: 1.02rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--cream, #f6f1e6);
  line-height: 1.2;
}

.home-dash-card-text {
  /* Claim a whole line so the title is never squeezed by the description. */
  flex: 1 1 100%;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--muted, rgba(246, 241, 230, 0.68));
  /* Clamped to two lines. The descriptions are 30-40 characters and a 10th of
     the shell is ~130px, so left alone they wrap to three or four lines and
     the row doubles in height. `-webkit-line-clamp` with an explicit
     `overflow: hidden` also degrades to a plain clip where it is unsupported,
     which is the same visual result rather than an overflow. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* ——— The importer as the primary action ———
   It sits under the shortcut cards in the left column and is the one thing a
   member opened the workspace to do, but it was still sized as a utility
   inset: a 0.92rem input and a 40px button, deliberately shrunk on the
   grounds that it sat below a full-width dashboard and outweighed it.

   That reasoning is gone. With the dashboard compact and beside it rather
   than above, the importer can afford to be a real control instead of a
   footnote, so its type and targets step back up toward the guest panel's
   scale. Declared here rather than in home_import.css because that sheet is
   shared with the guest landing page, where the panel is still a wide
   marketing band and must keep its own sizing. */
.home-dash .home-import--member {
  padding: 20px 22px;
  gap: 12px;
  /* Fill the tab (the column is a flex box), so the scanned-games list has
     the whole height to grow into rather than a stub. */
  flex: 1 1 auto;
  min-height: 0;
}

/* The form row goes two-up in the workspace, spending the panel's new width on
   a second column rather than on a longer line.

   The member panel is single-column by default (home_import.css), which was
   right while the panel was ~660px. At ~1190px it stretches the username input
   across more than a thousand pixels, which is the exact failure the guest
   card's own rule documents: "one input stretched across 1560px is neither a
   readable measure nor a usable target". The guest solved it with
   `.home-import--guest .home-import-row`; this is the same two columns, scoped
   to the workspace so the shared sheet still owns the guest's.

   0.85fr / 1.15fr rather than 1fr / 1fr: the source switch is two short
   labels, the field and its button need the room. */
.home-dash .home-import--member .home-import-row {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  align-items: end;
  gap: 11px;
}

/* Collapses with the panel rather than on the window: below the split
   breakpoint the two panels stack, so the importer gets the full frame and
   still has room for two columns — it only gives them up when the frame itself
   is too narrow, which is a measure question, not a layout one. Mirrors the
   guest card's 900px collapse, which is the same judgment at the same size. */
@media (max-width: 900px) {
  .home-dash .home-import--member .home-import-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
  }
}

.home-dash .home-import--member .home-import-head h2 {
  font-size: clamp(1.25rem, 1.8vw, 1.6rem);
}

.home-dash .home-import--member .home-import-head .eyebrow {
  font-size: 0.66rem;
}

.home-dash .home-import--member .home-import-head > p:not(.eyebrow) {
  margin-top: 8px;
  font-size: 0.95rem;
}

.home-dash .home-import--member .home-import-select,
.home-dash .home-import--member .home-import-input {
  padding: 12px 14px;
  font-size: 1rem;
}

/* 46px rather than 40px: comfortably above the 44px touch-target floor even
   with the panel's own padding and borders counted. */
.home-dash .home-import--member .home-import-go {
  min-height: 46px;
  padding: 0 20px;
  font-size: 1rem;
}

.home-dash .home-import--member .home-import-toggle-btn {
  padding: 9px 8px;
  font-size: 0.9rem;
}

.home-dash .home-import--member .home-import-field > span {
  margin-bottom: 5px;
  font-size: 0.68rem;
}

/* The scanned-games list is the tall part of the Import tab. The shared
   sheet's 420px cap is right for the guest band, but here it was a stub in
   the middle of a panel that has the whole window's height.

   So the workspace replaces that number with a viewport-derived one: the
   importer's furniture above the list (head, form, the two paddings) costs
   roughly 330px of a desktop shell, so `100dvh - --dash-chrome - 330px` is
   the height the results row has left on any window. It grows with the
   window and never beyond it, and the 1fr row below lets it shrink when the
   panel is shorter; the shared sheet's `overflow-y` keeps a long scan
   scrolling INSIDE the box instead of growing the page.

   The cap must be definite and viewport-derived rather than `none`: an
   unbounded list made the grid rows grow with their content, the panel
   passed its cap, and the whole workspace ran down the page. */
.home-dash .home-import--member .home-import-results {
  max-height: calc(100dvh - var(--dash-chrome) - 330px);
}

.home-dash .home-import--member:has(.home-import-results:not([hidden])) {
  grid-template-rows: auto auto auto minmax(0, 1fr);
}

/* ——— Import / linked account row ——— */

.home-dash-import {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 22px;
  flex-wrap: wrap;
  margin-top: 22px;
  padding: 22px 24px;
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--gold);
  border-radius: 16px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 4%, transparent), transparent 70%),
    color-mix(in srgb, var(--panel) 90%, var(--bg-2));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 5%, transparent);
}

.home-dash-import-copy {
  min-width: 0;
  flex: 1 1 320px;
}

.home-dash-import-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--cream, #f6f1e6);
}

.home-dash-import-text {
  margin: 5px 0 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--muted, rgba(246, 241, 230, 0.68));
  overflow-wrap: anywhere;
}

.home-dash-import-btn {
  white-space: nowrap;
}

/* The practice dashboard: the right-hand box.

   Given the SAME panel surface as `.home-dash-col` beside it — same padding,
   same radius, same machined top light — because the two are a pair. The
   left column carries that surface on its own wrapper; this one is a
   `<section class="om-dash">` from the shared partial, so the surface has to
   be declared here.

   No top margin: a margin would drop it below the left column's top edge,
   and the two boxes would stop reading as a row. */
.home-dash .home-practice-dash {
  margin: 0;
  min-width: 0;
  padding: 16px 18px 18px;
  border-radius: 20px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 4%, transparent), transparent 200px),
    color-mix(in srgb, var(--panel) 92%, var(--bg-2));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 7%, transparent);
}

/* ——— The active panel scrolls, the page does not ———
   The shell is capped to the viewport (see `.home-dash`), and the active
   panel can hold more than that: a member with a dozen openings, or a scan
   that returned twenty-five games. The panel scrolls INSIDE the shell rather
   than pushing the page taller, which is what actually delivers "no page
   scroll" — the cap alone would just clip.

   The scrolling rules themselves live on `html.home-js .home-dash-panel.is-active`
   up with the tabbed layout they belong to. `max-height: 100%` only binds when
   the panel is a stretched grid item, which is exactly what the tabbed mode
   makes it — and exactly what a content-sized no-JS block is not.

   `overscroll-behavior: contain` matters as much as the overflow: without it,
   reaching the end of a list chains the scroll to the page and the workspace
   jumps, which reads as a bug even though each panel behaves correctly. */

/* The importer is the last block in the left column, so it carries the gap
   the column's own layout does not: the header and the cards have margins
   above them, and this one would otherwise sit flush against the card row. */
.home-dash .home-dash-col > .home-import {
  margin-top: 14px;
  flex: 1 1 auto;
  min-height: 0;
}

/* ——— The dashboard in a split column ———
   style_tracking.css sizes this panel for a full-width shell: six metric
   tiles across, ~90px of padding on the card, and two opening lists that
   grow with however many openings a member has. In the split it gets half
   the frame, and at narrower frames all three of those assumptions break —
   the labels wrap and the lists push the panel past the budget.

   So it is declared here rather than inherited. Two mechanisms:

     · A CONTAINER query, not a viewport one. The panel's width comes from
       the split grid, not the window, so keying off the viewport is simply
       the wrong signal — the same panel is ~1190px on a wide monitor and
       ~570px on a 1200px one, and only one of those wants three-up tiles.
       The query is scoped to .home-practice-dash so it outweighs the bare
       .om-dash-metrics rule in style_tracking.css, which loads after this
       sheet.

     · The compact layout as the panel's own declaration, rather than
       something the query turns on. A compaction gated on a query match
       would leave the panel unstyled in the narrow case it was written for,
       which is the case that actually needs it.

   The 900px threshold is where six tiles stop being six tiles: below it
   each tile is under 120px and "Lines mastered" wraps. Above it — which is
   where the widened frame puts a full-size monitor — the query does not
   match and the tiles go back to the six-up row the panel was designed
   around. */

.home-dash .home-practice-dash {
  container-type: inline-size;
}

@container (max-width: 900px) {
  .home-practice-dash .om-dash-metrics {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ——— Sizing for the Practice tab ———
   The practice dashboard lives in its own full-screen tab now, so the
   shrink-to-fit measures of the split-column era are gone. It gets a roomier
   surface and larger tile typography, matching the welcome tab's scale. The
   shell's cap is enforced by the panel scroll (see
   `html.home-js .home-dash-panel.is-active`), not by starving the dashboard
   of padding. The container query above still handles narrow frames, where
   six tiles stop being six tiles. */
.home-practice-dash .om-dash {
  padding: 20px 22px 22px;
}

/* The heading block reads at the same scale as the welcome header: kicker on
   top, a larger title, the lead back under it. */
.home-practice-dash .om-dash-head {
  margin-bottom: 16px;
}

.home-practice-dash .om-dash-title {
  font-size: clamp(1.6rem, 2.6vw, 2.2rem);
}

.home-practice-dash .om-dash-lead {
  display: block;
  font-size: 1rem;
}

.home-practice-dash .om-dash-metrics {
  gap: 12px;
}

.home-practice-dash .om-metric {
  padding: 13px 14px;
}

.home-practice-dash .om-metric-label {
  margin-bottom: 4px;
  font-size: 0.72rem;
}

.home-practice-dash .om-metric strong {
  font-size: 1.4rem;
}

.home-practice-dash .om-dash-panels,
.home-practice-dash .om-dash-openings {
  gap: 14px;
}

/* The week chips keep seven across but gain real presence: a row of seven
   that still reads at a glance, with a bigger mark. */
.home-practice-dash .om-week-chip {
  padding: 10px 6px;
  gap: 7px;
  font-size: 0.72rem;
}

.home-practice-dash .om-week-chip b {
  font-size: 0.9rem;
}

/* The two opening lists are unbounded by design — they grow with the member's
   repertoire. Here they are capped and scroll inside their own panel, so a
   member with thirty openings does not push the whole workspace off screen.
   `min-height: 0` is required on the grid child for its `overflow` to bind;
   without it the item's automatic minimum size is its content's and the cap
   does nothing at all.

   260px rather than the 170px of the split-column era: the tab has the whole
   panel's height, so the lists show six or seven openings before the first
   scroll instead of two or three. */
.home-practice-dash .om-dash-openings .om-opening-list {
  max-height: 260px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

/* List rows step up with the panel: taller rows, a larger opening name. */
.home-practice-dash .om-dash-openings .om-opening-list a {
  padding: 12px 14px;
}

.home-practice-dash .om-dash-openings .om-opening-list strong {
  font-size: 1rem;
}

.home-practice-dash .om-panel {
  padding: 14px 16px;
}

.home-practice-dash .om-dash-title {
  color: var(--cream, #f6f1e6);
  letter-spacing: -0.015em;
}

.home-practice-dash .om-dash-kicker {
  letter-spacing: 0.16em;
  font-size: 0.7rem;
}

/* The dashboard sits inside the console shell, so its tiles get the same
   machined treatment as the shortcut cards — one surface language, not two. */
.home-practice-dash .om-metric,
.home-practice-dash .om-panel {
  border-color: var(--line-strong);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 4%, transparent), transparent 70%),
    color-mix(in srgb, var(--panel) 88%, var(--bg-2));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 5%, transparent);
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}

.home-practice-dash .om-metric {
  border-radius: 14px;
}

.home-practice-dash .om-panel {
  border-radius: 16px;
}

.home-practice-dash .om-metric:hover,
.home-practice-dash .om-panel:hover {
  border-color: color-mix(in srgb, var(--gold) 26%, var(--line-strong));
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 7%, transparent),
    0 8px 22px -14px rgba(0, 0, 0, 0.55);
}

/* Tabular figures so the numbers do not jitter as they update. */
.home-practice-dash .om-metric strong {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--cream, #f6f1e6);
}

/* ——— Per-tile accent ———
   Each metric gets its own hue drawn from the theme accent, so the row reads as
   a colour-coded set without introducing a second palette. The ramp is a
   cool analogous sweep (blue → cyan → teal), which stays inside the shell's
   temperature instead of spinning through the whole wheel.

   Relative colour syntax (hsl(from …)) is only in newer browsers, so every
   enhanced declaration is preceded by a plain fallback. An older engine drops
   the second declaration and keeps the first rather than losing the tile. */
.home-practice-dash .om-metric {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  --tile-accent: var(--gold);
}

.home-practice-dash .om-metric::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(120% 100% at 12% 0%, color-mix(in srgb, var(--tile-accent) 16%, transparent), transparent 62%);
  opacity: 0.9;
  transition: opacity 0.24s ease;
}

.home-practice-dash .om-metric:hover::before {
  opacity: 1;
}

/* A 3px accent rail on the leading edge: the one element that makes the tiles
   read as a colour-coded set at a glance.

   The insets are proportional to the tile, not fixed at 14px. The tiles are
   ~46px tall here rather than the ~90px they were sized for, so a 14px inset
   would leave the rail 18px long on a 46px tile — a stub, not a rail. */
.home-practice-dash .om-metric::after {
  content: "";
  position: absolute;
  top: 22%;
  bottom: 22%;
  left: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--tile-accent);
  opacity: 0.85;
  transition: opacity 0.24s ease, top 0.24s ease, bottom 0.24s ease;
}

.home-practice-dash .om-metric:hover::after {
  opacity: 1;
  top: 12%;
  bottom: 12%;
}

/* Fallback ramp: plain tints of the theme accent. Always valid. */
.home-practice-dash .om-metric:has(#om-metric-day) { --tile-accent: color-mix(in srgb, var(--gold) 92%, #ffffff); }
.home-practice-dash .om-metric:has(#om-metric-week) { --tile-accent: color-mix(in srgb, var(--gold) 84%, #ffffff); }
.home-practice-dash .om-metric:has(#om-metric-all) { --tile-accent: color-mix(in srgb, var(--gold) 76%, #ffffff); }
.home-practice-dash .om-metric:has(#om-metric-accuracy) { --tile-accent: color-mix(in srgb, var(--gold) 68%, #ffffff); }
.home-practice-dash .om-metric:has(#om-metric-mastered) { --tile-accent: color-mix(in srgb, var(--gold) 60%, #ffffff); }
.home-practice-dash .om-metric:has(#om-metric-run) { --tile-accent: color-mix(in srgb, var(--gold) 52%, #ffffff); }

/* Progressive enhancement: a wider spread across the ramp, each tile mixed
   toward a fixed cool violet. Relative colour syntax only reads a channel
   verbatim — `calc()` inside hsl(from …) is rejected outright — so the spread
   is expressed as mixes rather than hue arithmetic. Browsers without relative
   colour support simply keep the tint ramp above. */
@supports (color: hsl(from red h s l)) {
  .home-practice-dash .om-metric:has(#om-metric-day) { --tile-accent: color-mix(in srgb, var(--gold) 94%, #ffffff); }
  .home-practice-dash .om-metric:has(#om-metric-week) { --tile-accent: color-mix(in srgb, var(--gold) 62%, #a5b4fc); }
  .home-practice-dash .om-metric:has(#om-metric-all) { --tile-accent: color-mix(in srgb, var(--gold) 46%, #818cf8); }
  .home-practice-dash .om-metric:has(#om-metric-accuracy) { --tile-accent: color-mix(in srgb, var(--gold) 34%, #6366f1); }
  .home-practice-dash .om-metric:has(#om-metric-mastered) { --tile-accent: color-mix(in srgb, var(--gold) 26%, #8b5cf6); }
  .home-practice-dash .om-metric:has(#om-metric-run) { --tile-accent: color-mix(in srgb, var(--gold) 20%, #a855f7); }
}

/* The label picks up a hint of its tile's hue so the row reads as one system. */
.home-practice-dash .om-metric-label {
  color: color-mix(in srgb, var(--tile-accent, var(--gold)) 62%, var(--muted));
}

.home-practice-dash .om-metric strong {
  color: color-mix(in srgb, var(--tile-accent, var(--gold)) 30%, var(--cream, #f6f1e6));
}

/* The streak tile is the habit metric, so it keeps the shell's live pip. */
.home-practice-dash .om-metric:has(#om-metric-run) strong::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 8px;
  border-radius: 50%;
  background: var(--tile-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tile-accent) 22%, transparent);
  animation: om-tile-pulse 2.4s ease-in-out infinite;
  vertical-align: middle;
}

@keyframes om-tile-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.55; transform: scale(0.82); }
}

/* Panels take a single restrained wash each, keyed off their own role. The
   plain color-mix version is the baseline; the hue-shifted one is progressive. */
.home-practice-dash .om-panel-heat {
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--cream) 4%, transparent), transparent 70%),
    radial-gradient(110% 90% at 88% 0%, color-mix(in srgb, var(--gold) 10%, transparent), transparent 60%),
    color-mix(in srgb, var(--panel) 88%, var(--bg-2));
}

@supports (color: hsl(from red h s l)) {
  .home-practice-dash .om-panel-heat {
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--cream) 4%, transparent), transparent 70%),
      radial-gradient(110% 90% at 88% 0%, color-mix(in srgb, #818cf8 10%, transparent), transparent 60%),
      color-mix(in srgb, var(--panel) 88%, var(--bg-2));
  }
}

.home-practice-dash .om-panel-head h3 {
  letter-spacing: -0.01em;
}

/* Escape hatch back to the public marketing homepage. Deliberately quiet --
   the workspace should not advertise the landing page, but it must not be a
   dead end either.

   Placed in the header's second column under the sub, so it costs the header
   one line instead of a block of its own. */
.home-dash-public-link {
  grid-column: 2;
  grid-row: 3;
  justify-self: start;
  display: inline-block;
  margin: 2px 0 0;
  font-size: 0.8rem;
  color: var(--muted, rgba(246, 241, 230, 0.68));
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.home-dash-public-link:hover,
.home-dash-public-link:focus-visible {
  color: var(--gold-bright);
  border-bottom-color: currentColor;
}

@media (max-width: 1200px) {
  /* The shell no longer has padding of its own -- it is an unframed grid whose
     two children are the panels. The gap is still its job. */
  .home-dash-inner {
    gap: 14px;
  }

  /* Four across, one step down from the six of a full frame. At this width each
     panel is ~570px, where five across would give ~110px per card — under the
     ~130px the card was drawn for, which is the cramping the base rule's
     comment is about. */
  .home-dash-actions {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* The tabbed workspace gives up the side-by-side arrangement before the
   cards give up their own columns: on a window this narrow the rail would
   eat a quarter of the width, so instead it becomes a horizontal strip
   above the panels.

   This is also where the viewport cap is lifted (see `.home-dash` above).
   Stacked, everything is roughly twice the height of the side-by-side
   layout, so capping it to the window would bury most of the dashboard
   behind an internal scrollbar — on a narrow screen, scrolling the page is
   the correct behaviour, so the cap is simply not applied. */
@media (max-width: 1100px) {
  html.home-js .home-dash-inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "tabs" "panels";
    gap: 12px;
  }

  /* The rail leaves the left edge and becomes a horizontal strip. The four
     buttons can still outrun a narrow window, so the strip scrolls sideways
     instead of wrapping into two ragged rows. */
  html.home-js .home-dash-tabs {
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: thin;
    padding-bottom: 2px;
  }

  html.home-js .home-dash-panels {
    grid-area: panels;
  }

  /* Narrow screens scroll the page naturally again -- the shell's cap is
     lifted (see `.home-dash` above), so the panel must not trap a scroll
     area of its own. The scanned-games list keeps its own viewport-derived
     cap (declared with the importer above), so a long scan still scrolls
     inside the box rather than as a mile-long page. */
  html.home-js .home-dash-panel.is-active {
    max-height: none;
  }

  .home-dash-tab {
    white-space: nowrap;
  }
}

@media (max-width: 560px) {
  .home-dash-inner {
    gap: 12px;
  }

  /* The panels keep their own padding at every width -- they ARE the boxes now,
     so their inner padding is what the phone rule is really about. */
  .home-dash .home-dash-col,
  .home-dash .home-practice-dash {
    padding: 14px 14px 16px;
    border-radius: 16px;
  }

  .home-dash-actions {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  /* Back to a comfortable single-column card: the width is there again, so
     the compact padding of the five-across grid only looks cramped. */
  .home-dash-card {
    padding: 14px 14px 15px;
  }

  .home-dash-import {
    align-items: flex-start;
    padding: 18px 16px;
  }

  .home-dash-import-btn {
    width: 100%;
    text-align: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .home-dash-card,
  .home-practice-dash .om-metric,
  .home-practice-dash .om-panel,
  .home-practice-dash .om-heat-cell {
    transition: none;
  }

  .home-dash-card:hover,
  .home-dash-card:focus-visible {
    transform: none;
  }

  .home-practice-dash .om-heat-cell:hover {
    transform: none;
  }

  /* The sheen and the pips are pure decoration, so they never animate in. */
  .home-dash-card::after {
    display: none;
  }

  .home-dash-eyebrow::before,
  .home-practice-dash .om-metric:has(#om-metric-run) strong::after {
    animation: none;
  }
}

/* ——— Pointer-tracked sheen ———
   The shortcut cards highlight wherever the cursor actually is, so the hover
   reads as the surface catching light rather than a canned animation.

   Uses a single delegated pointermove listener and writes two custom
   properties, so there is exactly one handler no matter how many cards render.
   Guarded for reduced motion, and skipped entirely on coarse pointers, where
   there is no cursor to track. Everything degrades to the static CSS sheen. */
.home-dash-js .home-dash-actions {
  pointer-events: auto;
}

@media (hover: hover) and (pointer: fine) {
  @media (prefers-reduced-motion: no-preference) {
    .home-dash-card {
      --mx: 50%;
      --my: 0%;
    }
  }
}

/* ——— Premium glow on the scrollable regions ———
   The workspace has two places content scrolls inside a fixed-height frame:
   the importer's results list, and the 90-day heatmap (13 weeks of cells in a
   strip narrower than the panel). Both are easy to miss — one just looks like
   a short list, the other like a dead strip. A soft blue halo around the frame
   marks "there is more in here, scroll", without a scrollbar being mandatory
   to notice.

   The hue is the site's accent blue (--gold is that name in this codebase —
   a legacy name, but changing it is a much larger refactor than this warrants),
   so the glow sits in the same family as the Plus pill and the active nav.

   Three details that matter:
     * `overflow: hidden` is deliberately NOT used. The glow lives in the
       element's own box-shadow, so a clipped container would cut the halo off
       at the frame edge and it would read as a hard outline instead.
     * The intensity is themed, because one set of values cannot serve both.
       A tight translucent halo only reads against a dark page; the same values
       on the near-white light theme are a pale blue smear that all but
       disappears. So the light theme gets a darker accent, a stronger ring and
       a wider spread, which is what a shadow needs to be seen on white.
     * Only the FRAME glows. The rows inside keep their own surfaces, so no
       text sits on a tinted background and contrast is untouched. */
.home-dash {
  /* One knob per layer, so the intensity is tunable in a single place. */
  --dash-glow-ring: 1px;
  --dash-glow-edge: color-mix(in srgb, var(--gold) 30%, transparent);
  --dash-glow-near: color-mix(in srgb, var(--gold) 34%, transparent);
  --dash-glow-far: color-mix(in srgb, var(--gold) 26%, transparent);
  --dash-glow-hot-ring: color-mix(in srgb, var(--gold) 44%, transparent);
  --dash-glow-hot-near: color-mix(in srgb, var(--gold) 46%, transparent);
  --dash-glow-hot-far: color-mix(in srgb, var(--gold) 32%, transparent);

  --dash-glow:
    0 0 0 var(--dash-glow-ring) var(--dash-glow-edge),
    0 0 18px -2px var(--dash-glow-near),
    0 0 46px -10px var(--dash-glow-far);
  --dash-glow-hot:
    0 0 0 1px var(--dash-glow-hot-ring),
    0 0 24px -2px var(--dash-glow-hot-near),
    0 0 58px -10px var(--dash-glow-hot-far);
}

/* Light theme. A shadow on white needs a deeper hue, a solid ring to read as
   an edge at all, and more spread to register as a glow rather than a tint. */
[data-theme="light"] .home-dash {
  --dash-glow-ring: 1.5px;
  --dash-glow-edge: color-mix(in srgb, var(--gold) 55%, transparent);
  --dash-glow-near: color-mix(in srgb, var(--gold) 40%, transparent);
  --dash-glow-far: color-mix(in srgb, var(--gold) 30%, transparent);
  --dash-glow-hot-ring: color-mix(in srgb, var(--gold) 78%, transparent);
  --dash-glow-hot-near: color-mix(in srgb, var(--gold) 52%, transparent);
  --dash-glow-hot-far: color-mix(in srgb, var(--gold) 38%, transparent);
}

/* The importer's results list. */
.home-dash .home-import-results {
  border-color: color-mix(in srgb, var(--gold) 26%, var(--line-strong));
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 6%, transparent),
    var(--dash-glow);
  transition: border-color 0.22s ease, box-shadow 0.22s ease;
}

.home-dash .home-import-results:hover,
.home-dash .home-import-results:focus-within {
  border-color: color-mix(in srgb, var(--gold) 46%, var(--line-strong));
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 8%, transparent),
    var(--dash-glow-hot);
}

/* The heatmap strip. It scrolls horizontally, so the glow doubles as the
   affordance for that. */
.home-dash .om-heat-wrap {
  position: relative;
  border: 1px solid color-mix(in srgb, var(--gold) 22%, var(--line-strong));
  border-radius: 12px;
  padding: 12px 10px 8px;
  box-shadow: var(--dash-glow);
  transition: border-color 0.22s ease, box-shadow 0.22s ease;
}

.home-dash .om-heat-wrap:hover,
.home-dash .om-heat-wrap:focus-within {
  border-color: color-mix(in srgb, var(--gold) 44%, var(--line-strong));
  box-shadow: var(--dash-glow-hot);
}

/* The two opening lists (Best openings / Needs work). These are grid lists
   rather than fixed-height scrollers today, so the glow here is the same
   premium accent rather than a scroll affordance — it keeps the practice
   dashboard reading as one lit set. */
.home-dash .om-dash-openings .om-panel {
  border-color: color-mix(in srgb, var(--gold) 24%, var(--line-strong));
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 5%, transparent),
    var(--dash-glow);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.home-dash .om-dash-openings .om-panel:hover {
  border-color: color-mix(in srgb, var(--gold) 42%, var(--line-strong));
  box-shadow: var(--dash-glow-hot);
}

/* The scrollbars inside the glowing frames pick up the same blue, so the
   affordance and the frame agree. */
.home-dash .home-import-results::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--gold) 46%, transparent);
}

.home-dash .home-import-results {
  scrollbar-color: color-mix(in srgb, var(--gold) 48%, transparent) transparent;
}

.home-dash .om-heat-wrap::-webkit-scrollbar {
  height: 8px;
}

.home-dash .om-heat-wrap::-webkit-scrollbar-track {
  background: transparent;
}

.home-dash .om-heat-wrap::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--gold) 46%, transparent);
  border-radius: 999px;
}

/* ——— Rating trends inside its own workspace tab ———
   The charts used to sit at the bottom of the practice dashboard, separated
   by the divider and top margin below; as a tab the charts ARE the content,
   so both go away. */
.home-dash-panel .home-rating-charts {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* Empty state (no linked account yet): a centred card that names the value
   of the tab and sends the member to /profile to link a site. */
.home-trends-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 1 1 auto;
  min-height: 0;
  padding: 48px 24px;
  text-align: center;
  border: 1px dashed var(--line-strong);
  border-radius: 20px;
  background: color-mix(in srgb, var(--panel) 92%, var(--bg-2));
}

.home-trends-icon {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin-bottom: 4px;
  border: 1px solid color-mix(in srgb, var(--gold) 24%, var(--line));
  border-radius: 14px;
  background: color-mix(in srgb, var(--gold) 9%, transparent);
  color: var(--gold);
}

.home-trends-icon svg {
  display: block;
  width: 24px;
  height: 24px;
}

.home-trends-cta h3 {
  margin: 0;
  font-family: var(--serif, Georgia, "Times New Roman", serif);
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  font-weight: 800;
  color: var(--cream, #f6f1e6);
}

.home-trends-cta p {
  margin: 0 0 16px;
  max-width: 46ch;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--muted, rgba(246, 241, 230, 0.68));
}

/* ——— Rating trends charts ——— */
.home-rating-charts {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.home-rating-charts-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.home-rating-charts-head h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--cream);
}
.home-rating-icon svg {
  display: block;
  width: 20px;
  height: 20px;
  color: var(--track-gold, var(--gold));
  opacity: 0.72;
}
.home-rating-charts-note {
  margin: 0;
  margin-left: auto;
  color: var(--muted);
  font-size: 0.78rem;
}

.home-rating-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.home-rating-panel {
  background: color-mix(in srgb, var(--bg-2) 50%, transparent);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px;
  min-height: 0;
}
.home-rating-panel-header {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
  font-size: 0.85rem;
  color: var(--cream);
}
.home-rating-logo {
  display: inline-flex;
  align-items: center;
}


.home-rating-charts-grid {
  display: block;
  min-height: 130px;
}

/* Time-control filter chips */
.home-rating-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 10px;
}

[data-pool="bullet"] { --chip-color: #ef4444; }
[data-pool="blitz"] { --chip-color: #e6a817; }
[data-pool="rapid"] { --chip-color: #38bdf8; }
[data-pool="daily"] { --chip-color: #22c55e; }
.home-rating-chip[data-pool] {
  border-left: 3px solid var(--chip-color);
  transition: border-left-color 0.2s, box-shadow 0.2s, border-color 0.2s, color 0.2s, background 0.2s;
}
.home-rating-chip.active[data-pool] {
  border-left-color: var(--chip-color);
}
.home-rating-panel:hover .home-rating-logo svg {
  filter: drop-shadow(0 0 8px var(--data-win-fill, #22c55e));
}
.home-rating-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 12px;
  font-size: 0.72rem;
  font-weight: 700;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font: inherit;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  transition: border-color 0.2s, color 0.2s, background 0.2s, box-shadow 0.2s;
}
.home-rating-chip.active {
  border-color: var(--gold);
  color: var(--gold-bright);
  background: color-mix(in srgb, var(--gold) 16%, var(--bg-2));
  box-shadow: 0 0 8px color-mix(in srgb, var(--gold) 30%, transparent);
}
.home-rating-chip:hover {
  border-color: color-mix(in srgb, var(--gold) 60%, var(--line));
  color: var(--cream);
  box-shadow: 0 0 4px color-mix(in srgb, var(--gold) 20%, transparent);
}

/* Blinking live dot */
@keyframes home-rating-live-pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.8; }
}
.chart-dot-ring {
  animation: home-rating-live-pulse 2s ease-in-out infinite;
}

/* Glowing panel header */
.home-rating-panel-header {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
  font-size: 0.85rem;
  color: var(--cream);
}
.home-rating-logo {
  display: inline-flex;
  align-items: center;
  transition: filter 0.3s;
}
.home-rating-panel-header strong {
  font-weight: 700;
  color: var(--cream);
  text-shadow: 0 0 6px color-mix(in srgb, var(--data-win-fill, #22c55e) 50%, transparent);
  transition: text-shadow 0.3s;
}

.home-rating-charts-loading {
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
  padding: 12px;
  animation: home-rating-pulse 1.4s ease-in-out infinite;
}
.home-rating-charts-empty {
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
  padding: 12px;
}

@keyframes home-rating-pulse {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

.home-rating-chart {
  width: 100%;
}
.home-rating-chart-empty {
  background: var(--bg-2);
  border-radius: 8px;
  padding: 24px 12px;
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
  min-height: 100px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.home-rating-chart-empty span {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--cream);
}
.home-rating-chart-empty a {
  color: var(--gold-bright);
}

.home-rating-chart-label {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin-bottom: 2px;
}
.home-rating-chart-pool {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.home-rating-chart-current {
  font-size: 0.92rem;
  font-weight: 800;
  color: var(--cream);
}
.home-rating-chart-trend {
  font-size: 0.68rem;
  font-weight: 600;
  margin-left: auto;
  font-style: normal;
}
.home-rating-chart-trend.is-up {
  color: var(--data-win-fill, #22c55e);
}
.home-rating-chart-trend.is-down {
  color: var(--data-loss-fill, #ef4444);
}
.home-rating-chart-trend.is-flat {
  color: var(--muted);
}
.home-rating-chart-svg {
  display: block;
  width: 100%;
  height: auto;
  color: var(--cream);
}
.home-rating-chart-range {
  font-family: inherit;
  font-size: 7px;
  fill: var(--muted);
}
.home-rating-chart-range-value {
  font-family: inherit;
  font-size: 7px;
  font-weight: 700;
  fill: var(--muted);
}

@media (max-width: 640px) {
  .home-rating-grid {
    grid-template-columns: 1fr;
  }
}