/* ============================================================
   Stackpeek — site-only styles

   stackpeek.css is the shared design-system contract: copied byte-for-byte
   from docs/stackpeek-design-system-files/project/, and the browser extension
   re-uses its :root token block. Never hand-edit it — test/assets/
   design_system_sync_test.rb will fail if you do.

   This file holds the four things the design artefact has no styles for,
   because they exist only on the Rails site: the auth pages, their stacked
   form layout, page-level flash messages, and long-form prose. Colour, type,
   spacing and duration come from --sp-* tokens only.
   ============================================================ */

/* ============================================================
   1 — Auth pages
   ============================================================ */

.sp-auth {
  max-width: 420px;
  margin: 0 auto;
  padding: var(--sp-space-8) 0;
}

.sp-auth__title {
  font-size: var(--sp-fs-2xl);
  font-weight: var(--sp-fw-semibold);
  line-height: var(--sp-lh-tight);
  letter-spacing: var(--sp-tracking-tight);
}

.sp-auth__lede {
  margin-top: var(--sp-space-3);
  color: var(--sp-text-muted);
  line-height: var(--sp-lh-normal);
}

.sp-auth__alt {
  margin-top: var(--sp-space-5);
  font-size: var(--sp-fs-sm);
  color: var(--sp-text-faint);
}

/* ============================================================
   2 — Stacked form
   ============================================================ */

.sp-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-4);
  margin-top: var(--sp-space-6);
}

.sp-form__row {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
}

/* .sp-field is sized for the inline notify row (flex: 1 1 220px); a stacked
   row wants the full column instead. */
.sp-form .sp-field {
  flex: 0 0 auto;
  width: 100%;
}

.sp-form .sp-btn {
  align-self: flex-start;
}

/* ============================================================
   3 — Flash messages
   ============================================================ */

.sp-flash {
  margin-top: var(--sp-space-5);
  padding: var(--sp-space-3) var(--sp-space-4);
  border: var(--sp-hairline) solid var(--sp-accent-border);
  border-radius: var(--sp-radius-md);
  background: var(--sp-accent-bg);
  color: var(--sp-text);
  font-size: var(--sp-fs-sm);
}

/* An alert is a state, not a brand moment: it drops the accent tint and leans
   on the strong hairline so it reads as a plain notice rather than a warning
   colour the palette does not have. */
.sp-flash--alert {
  border-color: var(--sp-border-strong);
  background: var(--sp-bg-subtle);
}

/* ============================================================
   4 — Long-form prose
   ============================================================ */

.sp-prose {
  max-width: 68ch;
  color: var(--sp-text-muted);
  line-height: var(--sp-lh-normal);
}

.sp-prose > * + * { margin-top: var(--sp-space-4); }

.sp-prose h2 {
  margin-top: var(--sp-space-7);
  color: var(--sp-text);
  font-size: var(--sp-fs-xl);
  font-weight: var(--sp-fw-semibold);
  line-height: var(--sp-lh-snug);
  letter-spacing: var(--sp-tracking-tight);
}

.sp-prose h3 {
  margin-top: var(--sp-space-6);
  color: var(--sp-text);
  font-size: var(--sp-fs-lg);
  font-weight: var(--sp-fw-medium);
  line-height: var(--sp-lh-snug);
}

/* stackpeek.css strips list styling globally (ul, ol { list-style: none }),
   which is right for the panel's data rows and wrong for a policy page. */
.sp-prose ul {
  padding-left: var(--sp-space-5);
  list-style: disc;
}

.sp-prose li + li { margin-top: var(--sp-space-2); }

.sp-prose code {
  font-family: var(--sp-font-mono);
  font-size: var(--sp-fs-sm);
}

/* ============================================================
   5 — Brand mark
   ============================================================ */

/* .sp-brand aligns on the baseline, which is right for its text and wrong for
   a replaced element -- an image has no baseline of its own and would hang
   below the word. Sized in em so it tracks the brand text rather than pinning
   a pixel height the type can drift away from. */
.sp-brand__mark {
  align-self: center;
  width: 1.15em;
  height: 1.15em;
  flex: none;
}

/* ============================================================
   6 — Demo panel mark
   ============================================================ */

/* The homepage's demo panel illustrates the real side panel, and the real one
   leads with the mark. Sized to the panel's own 14px rather than to the label
   it sits in: .sp-panel__id is micro uppercase type, and matching it would
   shrink the mark well below what the extension shows.

   Only this new class is styled. .sp-panel__chrome and .sp-panel__id belong to
   stackpeek.css, and a rule here whose selector starts with either of them
   would trip the "does not redefine design-system classes" guard. */
.sp-panel__mark {
  width: 14px;
  height: 14px;
  vertical-align: middle;
  margin-right: 2px;
}

/* ============================================================
   Live demo panel — rotation and scanning state
   ============================================================ */

/* A Turbo Frame is display:inline by default, which would collapse the panel's
   own block layout inside it. It carries the heading, panel, next control and
   caption -- everything that names the store -- so it reproduces the column
   layout those had as direct children of .sp-panel-slot. */
turbo-frame#demo-panel {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-3);
}

/* The skeleton sits alongside the frame rather than inside it: Turbo replaces
   the frame's entire contents on navigation, so anything within it would be
   swapped away at the exact moment it is needed.

   .sp-demo-stage rather than .sp-panel-slot as the anchor: a rule here whose
   selector begins with a design-system class trips the "does not redefine
   design-system classes" guard, and this stage is a site-only concern. */
.sp-demo-stage .sp-panel--skeleton {
  display: none;
}
.sp-demo-stage.is-scanning .sp-panel--skeleton {
  display: block;
}
.sp-demo-stage.is-scanning turbo-frame#demo-panel {
  display: none;
}

/* Rows past a fold, in Apps and in Best sellers.

   The attribute is repeated deliberately. panel.css sets `display` on the
   folded elements at weights that differ per section: `.sp-panel .sp-cat` is
   (0,2,0) but `.sp-panel .sp-bs li` is (0,2,1), because of the element
   selector. A single attribute pair lost to the latter, so Apps folded and Best
   sellers silently did not. Doubling takes this to (0,3,0), above anything
   panel.css can put on a row, without naming element types this rule would then
   have to track.

   Attributes rather than classes throughout: panel.css is the only source of
   sp- class names, which test/views/panel_markup_test.rb pins.

   The folded rows keep their attribute in both states; what changes is
   `data-expanded` on the section. Overriding `display` to un-hide them would
   need the right value per element, and `revert` reaches past panel.css to the
   UA sheet — so the rule simply stops applying instead. */
[data-controller~="panel-fold"]:not([data-expanded]) [data-folded][data-folded] {
  display: none;
}

.sp-panel-actions {
  display: flex;
  justify-content: flex-start;
  max-width: 360px;
}

/* ============================================================
   Store Intelligence preview — the "on release" slot
   ============================================================ */

/* The tracks are the FAQ's: two equal columns over the wrap with a space-8
   gutter. .sp-states' own space-5 gutter put this section's divide at a
   position no other section shares, which is what made it read as misaligned
   -- the second column now starts exactly where the FAQ's does.

   column-gap rather than gap: the shorthand would also widen the row gap, and
   that gap is what separates the two states once they stack below 860px.

   A bare class rather than a rule scoped under .sp-states: a selector here
   beginning with a design-system class trips the "does not redefine
   design-system classes" guard. The markup carries both names instead. */
.sp-intel-states {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--sp-space-8);
}

/* One panel width inside those columns, so the two slots read as the same
   container before and after -- which is the section's whole claim. The track
   is wider than the panel; the alignment that matters is where each column
   starts. */
/* The slots are white cards, exactly as the hero's panel is -- but the hero
   lays its panel on a tinted wash and this section had nothing, so the same
   white read as flat here and as "clear" there. Tinting the ground is what the
   hero does to make a white panel look white; the difference was never in the
   cards. Flat rather than the hero's gradient: that one fades from the top of
   the page, where this section wants an even ground under two tall slots. */
#roadmap { background: var(--sp-accent-wash); }

.sp-intel-states .sp-slot { max-width: 420px; }
/* --sp-bg, not the bundle's --sp-surface: these slots stand in for the
   extension panel at the top of the page, and that panel's body is --sp-bg. The
   two tokens resolve to the same white in light mode, so the difference only
   showed in dark, where a slot sat a shade lighter than the panel it was
   imitating. The border is what separates a slot from the page, the same way it
   does for the panel above. Site-only, so it lives here rather than in the
   design bundle. */
.sp-intel-states .sp-slot { background: var(--sp-bg); }
.sp-intel-caption { max-width: 420px; }

.sp-intel-block { display: flex; flex-direction: column; gap: var(--sp-space-2); }
/* Flex so the change can sit against the right edge of the heading line, the
   way .sp-slot__head already pairs a title with a state. */
.sp-intel-block__hd {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-space-3);
  font-family: var(--sp-font-mono);
  font-size: var(--sp-fs-micro);
  letter-spacing: var(--sp-tracking-caps);
  text-transform: uppercase;
  color: var(--sp-text-faint);
}
/* No rules between ads rows. The metrics list above rules its rows because it
   is a long column of unrelated figures a reader scans down; this block is a
   short group about one thing, where the lines fence off three readings that
   belong together. The row padding still separates them. */
.sp-intel-ads .sp-metrics li { border-bottom: 0; }

/* Two slots stack in this column now, each with its own caption beneath it.
   .sp-state's own gap is the space between a slot and its caption; a sibling
   slot is a new reading of a different store and needs more air than that, or
   the caption reads as a header for the panel below it. */
.sp-intel-states .sp-slot ~ .sp-slot { margin-top: var(--sp-space-4); }

/* A metric row that opens onto its own chart. The summary reproduces the row
   layout .sp-metrics gives its plain siblings, so a charted row and an
   uncharted one sit on the same grid -- the only difference a reader should see
   is the caret. */
.sp-intel-metric__row,
.sp-intel-ads-chart__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--sp-space-2);
  align-items: baseline;
  cursor: pointer;
  list-style: none;
}
/* Safari still paints its own disclosure triangle without this, alongside ours. */
.sp-intel-metric__row::-webkit-details-marker,
.sp-intel-ads-chart__row::-webkit-details-marker { display: none; }
/* The caret is drawn here rather than left to the UA's marker, which cannot be
   positioned or coloured. Rotating on open is the whole affordance. */
.sp-intel-metric__row::before,
.sp-intel-ads-chart__row::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 4px solid currentColor;
  border-top: 3px solid transparent;
  border-bottom: 3px solid transparent;
  color: var(--sp-text-faint);
  transform: translateY(-1px);
  transition: transform 120ms ease;
}
.sp-intel-metric[open] .sp-intel-metric__row::before,
.sp-intel-ads-chart[open] .sp-intel-ads-chart__row::before {
  transform: translateY(-1px) rotate(90deg);
}

.sp-intel-metric__body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
  padding: var(--sp-space-3) 0 var(--sp-space-2);
}
.sp-intel-metric__hd {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-space-3);
  font-family: var(--sp-font-mono);
  font-size: var(--sp-fs-micro);
  letter-spacing: var(--sp-tracking-caps);
  text-transform: uppercase;
  color: var(--sp-text-faint);
}
.sp-intel-metric__change {
  font-variant-numeric: tabular-nums;
  color: var(--sp-text-muted);
}
/* The palette carries no red or green -- it is neutral plus one accent, and
   stackpeek.css is the token contract the extension copies verbatim, so adding
   semantic colours here would reach into the extension. Weight stands in:
   a movement against the metric takes full-strength text, a movement in its
   favour stays muted. The arrow and the hidden phrase carry the meaning; this
   only decides which one the eye lands on first. */
.sp-intel-metric__change[data-good="false"] { color: var(--sp-text); }
.sp-intel-metric__arrow { font-family: var(--sp-font-sans); }

/* No such utility existed in the bundle, and the judgement in each change has
   to reach a screen reader in words -- an arrow alone says "down", never
   "worse". clip-path rather than display:none, which would hide it from
   assistive tech as well as from sight. */
.sp-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* The definition an uncharted row hides behind a hint button. Here it is simply
   shown: the disclosure is already open, and a button inside a <summary> would
   be swallowed by the summary's own toggle. */
.sp-intel-metric__def {
  font-size: var(--sp-fs-xs);
  line-height: 1.45;
  color: var(--sp-text-muted);
  text-wrap: pretty;
}

/* Metric definitions. No JavaScript: the trigger reveals its adjacent hint
   through :hover and :focus. :focus rather than :focus-visible on purpose --
   :focus-visible is withheld on touch, and a tap is how a phone asks. */
.sp-intel-hint-host { position: relative; display: inline-block; }
.sp-intel-hint__trigger {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  /* Buttons do not inherit these from the UA stylesheet, and the block headings
     this sits in are uppercase with caps tracking -- without them the three
     block labels dropped to sentence case while the trend's stayed shouting.
     `inherit` is right in both homes: caps in a heading, plain in a metric row. */
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: help;
  /* Dotted rather than solid: this reveals a definition, it does not navigate,
     and a solid underline in a panel full of real links would promise one. */
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.sp-intel-hint {
  display: none;
  position: absolute;
  top: calc(100% + var(--sp-space-2));
  left: 0;
  z-index: 2;
  width: max-content;
  max-width: 260px;
  padding: var(--sp-space-2) var(--sp-space-3);
  border: var(--sp-hairline) solid var(--sp-border);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-surface);
  /* Stated, not inherited. The block headings this also sits inside are micro
     uppercase mono with caps tracking, and a paragraph of prose in that face is
     unreadable. */
  font-family: var(--sp-font-sans);
  font-size: var(--sp-fs-xs);
  font-weight: var(--sp-fw-regular);
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.45;
  color: var(--sp-text-muted);
  text-wrap: pretty;
}
/* Hints in the last block sit close enough to the panel's bottom edge that one
   opening downward hangs off the card. Opening upward keeps it inside. */
.sp-intel-block:last-child .sp-intel-hint {
  top: auto;
  bottom: calc(100% + var(--sp-space-2));
}

.sp-intel-hint__trigger:hover + .sp-intel-hint,
.sp-intel-hint__trigger:focus + .sp-intel-hint { display: block; }

/* Vertical breathing room for the dots and the tooltip: both are positioned
   against the plot's edges, and the extreme months sit exactly on top and
   bottom. Nothing here may clip -- a dot at y:100% is half outside the plot. */
.sp-intel-trend { padding: var(--sp-space-4) 0 var(--sp-space-2); }
.sp-intel-trend__plot { position: relative; height: 48px; }

/* preserveAspectRatio="none" lets the square view box stretch to whatever
   width the slot gives it. non-scaling-stroke keeps the line an even weight
   through that stretch; without it the horizontal scale thins the verticals. */
.sp-intel-trend svg { display: block; width: 100%; height: 100%; }
.sp-intel-trend polyline {
  fill: none;
  stroke: var(--sp-accent);
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

/* A full-height column centred on each point, so the target is the width of a
   month rather than the width of a dot. */
.sp-intel-trend__hit {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 20%;
  transform: translateX(-50%);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
/* The first and last months sit on the plot's edges, so a centred column there
   hangs half outside the card -- close enough to the border that the cursor
   picked up a tooltip while outside the panel entirely. Half-width and flush
   instead: 10 + 20x4 + 10 tiles the plot exactly, with no gap and no overhang. */
.sp-intel-trend__hit:first-of-type { width: 10%; transform: translateX(0); }
.sp-intel-trend__hit:last-of-type { width: 10%; transform: translateX(-100%); }

/* Dots are HTML, not SVG circles: the view box stretches on one axis only, so
   a circle inside it renders as an ellipse. The column is exactly as tall as
   the plot, so the point's y works unchanged as a percentage inside it -- which
   is why both axes run 0..100. */
.sp-intel-trend__dot {
  position: absolute;
  left: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: var(--sp-accent);
  opacity: 0;
  transition: opacity 120ms ease;
}
/* The edge columns are flush rather than centred, so their point is at the
   column's edge, not its middle. Without this the first and last dots sit half
   a column away from the line they mark. */
.sp-intel-trend__hit:first-of-type .sp-intel-trend__dot { left: 0; }
.sp-intel-trend__hit:last-of-type .sp-intel-trend__dot { left: 100%; }

.sp-intel-trend__hit:hover .sp-intel-trend__dot,
.sp-intel-trend__hit:focus-visible .sp-intel-trend__dot { opacity: 1; }

.sp-intel-trend__tip {
  position: absolute;
  bottom: 100%;
  transform: translate(-50%, -6px);
  z-index: 1;
  padding: 2px var(--sp-space-2);
  border: var(--sp-hairline) solid var(--sp-border);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-surface);
  font-family: var(--sp-font-mono);
  font-size: var(--sp-fs-micro);
  white-space: nowrap;
  color: var(--sp-text);
  pointer-events: none;
}

/* Position, term, volume. The position column is fixed so the terms align
   whether the rank is one digit or two. */
.sp-intel-kw { display: grid; }
.sp-intel-kw li {
  display: grid;
  grid-template-columns: 2.5ch minmax(0, 1fr) auto;
  gap: var(--sp-space-3);
  align-items: baseline;
  padding: var(--sp-space-2) 0;
  border-bottom: var(--sp-hairline) solid var(--sp-border);
  font-size: var(--sp-fs-sm);
}
.sp-intel-kw li:last-child { border-bottom: 0; }
.sp-intel-kw__pos {
  font-family: var(--sp-font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--sp-text-faint);
}
.sp-intel-kw__term { color: var(--sp-text); overflow-wrap: anywhere; }
.sp-intel-kw__vol {
  font-family: var(--sp-font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--sp-text-muted);
}

.sp-intel-pages { display: grid; }
.sp-intel-pages li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-space-3);
  align-items: baseline;
  padding: var(--sp-space-2) 0;
  border-bottom: var(--sp-hairline) solid var(--sp-border);
  font-size: var(--sp-fs-sm);
}
.sp-intel-pages li:last-child { border-bottom: 0; }
.sp-intel-pages__path {
  font-family: var(--sp-font-mono);
  color: var(--sp-text-muted);
  overflow-wrap: anywhere;
}
.sp-intel-pages__etv {
  font-family: var(--sp-font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--sp-text);
}
.sp-intel-pages__etv span { color: var(--sp-text-faint); }

.sp-intel-chips { display: flex; flex-wrap: wrap; gap: var(--sp-space-2); }
.sp-intel-chips li {
  padding: 2px var(--sp-space-2);
  border: var(--sp-hairline) solid var(--sp-border);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-bg-subtle);
  font-family: var(--sp-font-mono);
  font-size: var(--sp-fs-micro);
  color: var(--sp-text-muted);
}
/* Dashed, because a count is not one of the competitors it follows. */
.sp-intel-chips__more { border-style: dashed; color: var(--sp-text-faint); }

/* stackpeek.css collapses .sp-states to one column here; the override above
   would otherwise keep forcing two. Same breakpoint on purpose. */
@media (max-width: 860px) {
  .sp-intel-states { grid-template-columns: minmax(0, 1fr); }
}
