/* ==========================================================================
   StylePLUS PM — supplemental styles
   Loaded AFTER assets/css/styles.css.

   Why this file exists: styles.css is a PRECOMPILED Tailwind build that only
   contains the ~1900 classes the Metronic demos happened to use. There is no
   Tailwind step in this project (deploy is plain static + serverless), so any
   class not already in that build has to be defined here by hand.

   Before using a Tailwind class in a new page, check it exists:
       grep -x 'md:grid-cols-2' scratch/css-classes.txt
   or just add it below. Everything here is written against Metronic's own
   custom properties (--primary, --muted-foreground, …) so light and dark
   themes both work without a second set of rules.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Semantic tokens Metronic doesn't expose as variables.
   The palette values are lifted from its own --color-* scale so these sit in
   the same colour space as everything else rather than being eyeballed.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   0. Brand accent.
   Metronic ships a blue --primary. StylePLUS is green, so it is overridden
   here rather than by editing the precompiled styles.css.

   The logo's dominant green (#A3CD3B) is far too light to sit under white
   button text — 1.85:1, nowhere near AA. #64801E is the same hue (77°) taken
   darker to the point where BOTH directions pass: 4.52:1 under white text,
   and 4.40:1 as text on the dark sidebar/dark theme. That crossover is why a
   single value serves both themes instead of needing a light/dark pair.
   (Metronic's own blue managed only 3.68:1, so this is stricter than the
   template's baseline.)

   --brand keeps the true logo green available for decorative use, where
   contrast rules do not apply.
   -------------------------------------------------------------------------- */
:root {
  --primary:            #64801E;
  --primary-foreground: var(--color-white);
  --brand:              #A3CD3B;
}

.dark {
  --primary:            #64801E;
  --primary-foreground: var(--color-white);
}

:root {
  --success:            var(--color-green-600);
  --success-foreground: var(--color-white);
  --success-soft:       var(--color-green-50);
  --warning:            var(--color-yellow-600);
  --warning-foreground: var(--color-white);
  --warning-soft:       var(--color-yellow-50);
  --info:               var(--color-blue-600);
  --info-foreground:    var(--color-white);
  --info-soft:          var(--color-blue-50);
  --violet:             var(--color-violet-600);
  --violet-soft:        var(--color-violet-50);
}

.dark {
  --success-soft: color-mix(in oklch, var(--color-green-950)  70%, transparent);
  --warning-soft: color-mix(in oklch, var(--color-yellow-950) 70%, transparent);
  --info-soft:    color-mix(in oklch, var(--color-blue-950)   70%, transparent);
  --violet-soft:  color-mix(in oklch, var(--color-violet-950) 70%, transparent);
}

/* --------------------------------------------------------------------------
   2. Utility classes missing from the compiled build.
   Named exactly as Tailwind would, so that if a real build step is ever added
   these rules can simply be deleted rather than hunted down in the markup.
   -------------------------------------------------------------------------- */
.grid-cols-4     { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-cols-6     { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.line-clamp-1,
.line-clamp-2,
.line-clamp-3    { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.line-clamp-1    { -webkit-line-clamp: 1; }
.line-clamp-2    { -webkit-line-clamp: 2; }
.line-clamp-3    { -webkit-line-clamp: 3; }
.opacity-60      { opacity: .6; }
.opacity-70      { opacity: .7; }
.max-w-lg        { max-width: 32rem; }
.max-w-md        { max-width: 28rem; }
.max-w-xl        { max-width: 36rem; }
.min-w-0         { min-width: 0; }
.mt-0\.5         { margin-top: .125rem; }
.mt-1            { margin-top: .25rem; }
.gap-y-2         { row-gap: .5rem; }
.ms-3            { margin-inline-start: .75rem; }
.mt-8            { margin-top: 2rem; }
.tabular-nums    { font-variant-numeric: tabular-nums; }

/* Used throughout the dark sidebar. layout-10's markup references it, but the
   starter-kit build never compiled it — only .kt-link-inverse exists there.
   Scoped regions carry their own --foreground, so this resolves light inside
   the dark sidebar and dark on a light page, which is what "inverse" meant. */
.text-inverse    { color: var(--foreground); }
.whitespace-pre-wrap { white-space: pre-wrap; }

@media (min-width: 48rem) {
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .md\:flex        { display: flex; }
  .md\:hidden      { display: none; }
}
@media (min-width: 64rem) {
  .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .lg\:col-span-2  { grid-column: span 2 / span 2; }
}
@media (min-width: 80rem) {
  .xl\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .xl\:grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------------------
   3. Status, priority and area colour chips.
   One driver class (.sp-chip) plus a modifier, so a status badge is styled in
   exactly one place no matter which of the five screens renders it.
   -------------------------------------------------------------------------- */
.sp-chip {
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .1875rem .5rem; border-radius: .375rem;
  font-size: .75rem; font-weight: 500; line-height: 1.25rem;
  white-space: nowrap;
}
.sp-chip i { font-size: .8125rem; }

.sp-chip--planning    { background: var(--muted);        color: var(--muted-foreground); }
.sp-chip--active      { background: var(--info-soft);    color: var(--info); }
.sp-chip--on_hold     { background: var(--warning-soft); color: var(--warning); }
.sp-chip--blocked     { background: color-mix(in oklch, var(--destructive) 12%, transparent); color: var(--destructive); }
.sp-chip--done        { background: var(--success-soft); color: var(--success); }
.sp-chip--archived    { background: var(--muted);        color: var(--muted-foreground); }

.sp-chip--todo        { background: var(--muted);        color: var(--muted-foreground); }
.sp-chip--in_progress { background: var(--info-soft);    color: var(--info); }
.sp-chip--review      { background: var(--violet-soft);  color: var(--violet); }

.sp-chip--pending     { background: var(--muted);        color: var(--muted-foreground); }
.sp-chip--at_risk     { background: var(--warning-soft); color: var(--warning); }
.sp-chip--hit         { background: var(--success-soft); color: var(--success); }
.sp-chip--missed      { background: color-mix(in oklch, var(--destructive) 12%, transparent); color: var(--destructive); }

.sp-chip--confidential {
  background: color-mix(in oklch, var(--destructive) 10%, transparent);
  color: var(--destructive);
  border: 1px solid color-mix(in oklch, var(--destructive) 25%, transparent);
}

/* Priority reads as a dot rather than another badge — with a status chip
   already on every row, a second full badge turns the list into noise. */
.sp-priority {
  display: inline-flex; align-items: center; gap: .375rem;
  font-size: .75rem; color: var(--muted-foreground); white-space: nowrap;
}
.sp-priority::before {
  content: ''; width: .5rem; height: .5rem; border-radius: 9999px; flex: none;
  background: var(--muted-foreground);
}
.sp-priority--urgent::before { background: var(--destructive); }
.sp-priority--high::before   { background: var(--warning); }
.sp-priority--medium::before { background: var(--info); }
.sp-priority--low::before    { background: var(--color-zinc-400); }

/* --------------------------------------------------------------------------
   4. Initials avatar. No file uploads anywhere in this app — colour comes
   from users.avatar_color so a person's chip never changes on them.
   -------------------------------------------------------------------------- */
.sp-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: 9999px; flex: none;
  font-size: .75rem; font-weight: 600; letter-spacing: .01em;
  color: var(--color-white); background: var(--color-zinc-500);
  user-select: none;
}
.sp-avatar--sm { width: 1.5rem;  height: 1.5rem;  font-size: .625rem; }
.sp-avatar--lg { width: 2.75rem; height: 2.75rem; font-size: .9375rem; }

.sp-avatar--primary     { background: var(--primary); }
.sp-avatar--success     { background: var(--success); }
.sp-avatar--warning     { background: var(--warning); }
.sp-avatar--info        { background: var(--info); }
.sp-avatar--violet      { background: var(--violet); }
.sp-avatar--destructive { background: var(--destructive); }
.sp-avatar--zinc        { background: var(--color-zinc-500); }
/* Brand mark. 34px matches layout-10's own logo slot; the source art is 142px
   so it stays sharp on a retina display without a second asset. The ribbon is
   green in both themes, which is why the wordmark is NOT part of this crop —
   its "Style" is black and disappears against the dark sidebar. */
.sp-mark {
  height: 2.25rem; width: auto; flex: none; display: block;
}

.sp-avatar--empty {
  background: transparent; border: 1px dashed var(--input); color: var(--muted-foreground);
}

/* --------------------------------------------------------------------------
   5. Progress bar
   -------------------------------------------------------------------------- */
.sp-progress {
  height: .375rem; border-radius: 9999px; background: var(--muted);
  overflow: hidden; width: 100%;
}
.sp-progress__bar {
  height: 100%; border-radius: 9999px; background: var(--primary);
  transition: width .3s ease;
}
.sp-progress__bar--success { background: var(--success); }
.sp-progress__bar--warning { background: var(--warning); }
.sp-progress__bar--danger  { background: var(--destructive); }

/* --------------------------------------------------------------------------
   6. Task rows
   -------------------------------------------------------------------------- */
.sp-task {
  display: flex; align-items: flex-start; gap: .75rem;
  padding: .75rem .25rem; border-bottom: 1px solid var(--border);
}
.sp-task:last-child { border-bottom: 0; }
.sp-task:hover      { background: color-mix(in oklch, var(--accent) 50%, transparent); }
.sp-task--done .sp-task__title {
  text-decoration: line-through; color: var(--muted-foreground);
}
.sp-task__title  { font-size: .875rem; font-weight: 500; color: var(--foreground); }
.sp-task__meta   {
  display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
  margin-top: .25rem; font-size: .75rem; color: var(--muted-foreground);
}
.sp-task__check {
  width: 1.125rem; height: 1.125rem; margin-top: .125rem; flex: none;
  border-radius: 9999px; border: 1.5px solid var(--input);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; background: transparent; color: transparent;
  transition: all .15s ease; font-size: .625rem;
}
.sp-task__check:hover           { border-color: var(--success); color: var(--success); }
.sp-task__check[data-done="1"]  { background: var(--success); border-color: var(--success); color: var(--color-white); }

/* Overdue is the one thing on a row that must survive a fast scan. */
.sp-due--overdue { color: var(--destructive); font-weight: 500; }
.sp-due--today   { color: var(--warning);     font-weight: 500; }

/* --------------------------------------------------------------------------
   7. Cards / links / empty states
   -------------------------------------------------------------------------- */
/* Whole-card link, without wrapping the card in an <a>.
   A card that contains its own links (a website, a tag) cannot itself be an
   anchor — HTML forbids nested anchors and the parser silently tears the card
   apart. So the card stays a <div>, one inner link stretches an invisible
   ::after over the whole surface, and anything else that must stay clickable
   is lifted above it with .sp-above. */
.sp-link-card { display: block; position: relative;
                transition: border-color .15s ease, box-shadow .15s ease; }

.sp-stretch { text-decoration: none; color: inherit; }
.sp-stretch::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
}
.sp-stretch:hover { color: var(--primary); }

/* Sits above the stretched overlay so it remains independently clickable. */
.sp-above { position: relative; z-index: 2; }

/* Keyboard focus has to be visible on the card, since the focused element is
   now a link inside it rather than the card itself. */
.sp-link-card:focus-within { border-color: var(--primary); }
.sp-stretch:focus-visible { outline: none; }
.sp-link-card:hover {
  border-color: color-mix(in oklch, var(--primary) 40%, var(--input));
  box-shadow: 0 1px 3px rgb(0 0 0 / .06);
}

.sp-stat__value { font-size: 1.75rem; font-weight: 600; line-height: 1.1; color: var(--mono); }
.sp-stat__label { font-size: .8125rem; color: var(--muted-foreground); margin-top: .25rem; }

.sp-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .5rem; padding: 2.5rem 1rem; text-align: center; color: var(--muted-foreground);
}
.sp-empty i { font-size: 1.75rem; opacity: .5; }

/* --------------------------------------------------------------------------
   9. Login page
   -------------------------------------------------------------------------- */
.sp-login {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 1.5rem; background: var(--muted);
}
.sp-login__card {
  width: 100%; max-width: 25rem; background: var(--background);
  border: 1px solid var(--border); border-radius: .75rem;
  padding: 2rem; box-shadow: 0 4px 24px rgb(0 0 0 / .06);
}
.sp-login__brand {
  display: flex; flex-direction: column; align-items: center; gap: .375rem; margin-bottom: 1.75rem;
}
.sp-login__logo {
  width: 11rem; height: auto; display: block; margin-bottom: .5rem;
}

/* --------------------------------------------------------------------------
   10. Toasts — bottom-right, non-blocking. Never use alert(): a modal dialog
   in this app would block the extension-driven browser session.
   -------------------------------------------------------------------------- */
.sp-toasts {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 90;
  display: flex; flex-direction: column; gap: .5rem; pointer-events: none;
}
.sp-toast {
  pointer-events: auto; min-width: 16rem; max-width: 24rem;
  display: flex; align-items: flex-start; gap: .5rem;
  padding: .75rem .875rem; border-radius: .5rem;
  background: var(--mono); color: var(--mono-foreground);
  font-size: .8125rem; box-shadow: 0 8px 24px rgb(0 0 0 / .18);
  animation: sp-toast-in .18s ease-out;
}
.sp-toast--error   { background: var(--destructive); color: var(--color-white); }
.sp-toast--success { background: var(--success);     color: var(--color-white); }
@keyframes sp-toast-in { from { opacity: 0; transform: translateY(.5rem); } to { opacity: 1; transform: none; } }

/* Screen-reader-only, for labels the layout doesn't show. */
.sp-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   11. Competitive intelligence (§12).

   The one visual rule that carries the whole model: a COMPANY tag and a
   CATEGORY tag never look alike. Company is solid and leads with a storefront
   glyph; category is outlined and leads with a tag glyph. Every intel item
   carries both, so if the two read as one undifferentiated pile of chips the
   two-axis structure is invisible exactly where it matters most.
   -------------------------------------------------------------------------- */

/* Tags — the two axes. */
.sp-tags { display: flex; flex-wrap: wrap; align-items: center; gap: .3125rem; }
.sp-tag {
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .125rem .4375rem; border-radius: .3125rem;
  font-size: .6875rem; font-weight: 500; line-height: 1.5;
  white-space: nowrap; text-decoration: none; border: 1px solid transparent;
}
.sp-tag i { font-size: .75rem; opacity: .75; }
a.sp-tag:hover { filter: brightness(.94); }

/* Company: solid. */
.sp-tag--company {
  background: var(--mono); color: var(--mono-foreground); border-color: var(--mono);
}
.sp-tag--company.sp-tag--direct   { background: var(--destructive); border-color: var(--destructive); color: var(--color-white); }
.sp-tag--company.sp-tag--adjacent { background: var(--warning);     border-color: var(--warning);     color: var(--warning-foreground); }
.sp-tag--company.sp-tag--watch    { background: var(--muted-foreground); border-color: var(--muted-foreground); color: var(--color-white); }

/* Category: outlined. */
.sp-tag--category {
  background: transparent; color: var(--secondary-foreground); border-color: var(--border);
}
a.sp-tag--category:hover { border-color: var(--primary); color: var(--primary); filter: none; }

/* Significance — a dot, not another chip. Three chips per item is noise. */
.sp-sig {
  display: inline-flex; align-items: center; gap: .375rem;
  font-size: .6875rem; color: var(--muted-foreground); white-space: nowrap;
}
.sp-sig::before {
  content: ""; width: .5rem; height: .5rem; border-radius: 50%; background: var(--muted-foreground);
}
.sp-sig--high::before   { background: var(--destructive); box-shadow: 0 0 0 3px color-mix(in oklch, var(--destructive) 18%, transparent); }
.sp-sig--medium::before { background: var(--warning); }
.sp-sig--low::before    { background: var(--muted-foreground); opacity: .6; }

/* Stance — threat vs opportunity, the reading you take away. */
.sp-stance {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .6875rem; font-weight: 500; padding: .0625rem .375rem;
  border-radius: .25rem; white-space: nowrap;
}
.sp-stance--threat      { background: color-mix(in oklch, var(--destructive) 12%, transparent); color: var(--destructive); }
.sp-stance--opportunity { background: var(--success-soft);  color: var(--success); }
.sp-stance--neutral     { display: none; }   /* the default earns no ink */

/* Intel feed item. */
.sp-intel {
  display: block; padding: 1rem 1.125rem; border-bottom: 1px solid var(--border);
  text-decoration: none; color: inherit; transition: background .12s ease;
}
.sp-intel:last-child { border-bottom: 0; }
.sp-intel:hover { background: var(--muted); }
.sp-intel__head { display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem; }
.sp-intel__title { font-size: .875rem; font-weight: 500; color: var(--mono); }
.sp-intel__date {
  font-size: .6875rem; color: var(--muted-foreground); white-space: nowrap; padding-top: .125rem;
}
.sp-intel__kind {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .6875rem; color: var(--muted-foreground); text-transform: uppercase;
  letter-spacing: .02em; margin-bottom: .25rem;
}
.sp-intel__body {
  font-size: .8125rem; color: var(--secondary-foreground); margin-top: .375rem; line-height: 1.5;
}
/* The implication, set apart from the observation — the point of the record. */
.sp-intel__sowhat {
  display: flex; gap: .5rem; margin-top: .5rem; padding: .4375rem .625rem;
  background: var(--muted); border-left: 2px solid var(--primary); border-radius: 0 .25rem .25rem 0;
  font-size: .8125rem; color: var(--foreground);
}
.sp-intel__sowhat b { font-weight: 600; color: var(--primary); flex-shrink: 0; }
.sp-intel__foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: .625rem;
  margin-top: .625rem; font-size: .6875rem; color: var(--muted-foreground);
}
.sp-intel--unreviewed { background: color-mix(in oklch, var(--warning) 7%, transparent); }
.sp-intel--unreviewed:hover { background: color-mix(in oklch, var(--warning) 12%, transparent); }

/* Faceted filter bar. */
.sp-facets {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .75rem; margin-bottom: 1.25rem;
  background: var(--background); border: 1px solid var(--border); border-radius: .5rem;
}
.sp-facets .kt-select, .sp-facets .kt-input { width: auto; min-width: 8.5rem; }
.sp-facets__search { flex: 1 1 14rem; min-width: 12rem; }

/* Landscape matrix — companies down, attributes across. */
.sp-matrix { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.sp-matrix th, .sp-matrix td {
  padding: .625rem .75rem; text-align: left; border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.sp-matrix th {
  font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  color: var(--muted-foreground); white-space: nowrap; background: var(--muted);
}
.sp-matrix td.sp-matrix__num, .sp-matrix th.sp-matrix__num { text-align: right; font-variant-numeric: tabular-nums; }
.sp-matrix tbody tr:hover { background: var(--muted); }
.sp-matrix__name { font-weight: 500; color: var(--mono); }
.sp-matrix--us td { background: color-mix(in oklch, var(--primary) 8%, transparent); }
.sp-matrix--us .sp-matrix__name { color: var(--primary); }

/* Price band — where a company sits inside the category's full range. */
.sp-band { position: relative; height: .5rem; min-width: 6rem; background: var(--muted); border-radius: 999px; }
.sp-band__fill { position: absolute; top: 0; bottom: 0; background: var(--primary); border-radius: 999px; opacity: .75; }
.sp-band__point { position: absolute; top: -.125rem; width: .75rem; height: .75rem; border-radius: 50%;
                  background: var(--primary); border: 2px solid var(--background); }

/* Category tree card. */
.sp-cat { display: block; padding: 1rem 1.125rem; text-decoration: none; color: inherit; }
.sp-cat__name { font-size: .9375rem; font-weight: 500; color: var(--mono); }
.sp-cat__kids { display: flex; flex-wrap: wrap; gap: .375rem; margin-top: .625rem; }
.sp-cat__kid {
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .1875rem .5rem; border-radius: .3125rem; border: 1px solid var(--border);
  font-size: .75rem; color: var(--secondary-foreground); text-decoration: none;
}
.sp-cat__kid:hover { border-color: var(--primary); color: var(--primary); }
.sp-cat__kid b { font-weight: 600; color: var(--muted-foreground); }

/* Key/value spec pairs on a product row. */
.sp-kv { display: flex; flex-wrap: wrap; gap: .25rem .875rem; font-size: .75rem; color: var(--muted-foreground); }
.sp-kv span b { font-weight: 500; color: var(--secondary-foreground); }

/* Source rows. */
.sp-src { display: flex; align-items: center; gap: .75rem; padding: .625rem .75rem; border-bottom: 1px solid var(--border); }
.sp-src:last-child { border-bottom: 0; }
.sp-src__meta { font-size: .6875rem; color: var(--muted-foreground); }
.sp-src--off { opacity: .55; }

/* Provenance — sourcing is mandatory on every item, so it gets a fixed slot. */
.sp-prov { display: inline-flex; align-items: center; gap: .25rem; font-size: .6875rem; color: var(--muted-foreground); }
.sp-prov i { font-size: .75rem; }
.sp-prov--thin { color: var(--destructive); }   /* no source URL recorded */

/* Shared modal shell. Was inline styles on the one create-project dialog;
   the intel section needs several, and four copies of a 6-property style
   attribute is how they drift apart. */
.sp-modal {
  display: flex; position: fixed; inset: 0; z-index: 100;
  align-items: flex-start; justify-content: center;
  background: rgb(0 0 0 / .45); overflow-y: auto; padding: 3rem 1rem;
}
.sp-modal__panel {
  width: 100%; max-width: 34rem; background: var(--background);
  border: 1px solid var(--border); border-radius: .75rem;
}
.sp-modal__panel--wide { max-width: 46rem; }

/* Multi-select tag pickers in those modals. */
.sp-picker {
  display: flex; flex-wrap: wrap; gap: .375rem; padding: .5rem;
  border: 1px solid var(--input); border-radius: .5rem; max-height: 9rem; overflow-y: auto;
}
.sp-picker__opt {
  display: inline-flex; align-items: center; gap: .3125rem;
  padding: .1875rem .5rem; border-radius: .3125rem; border: 1px solid var(--border);
  font-size: .75rem; cursor: pointer; user-select: none; background: var(--background);
}
.sp-picker__opt:hover { border-color: var(--primary); }
.sp-picker__opt input { margin: 0; }
.sp-picker__opt--on { background: var(--primary); border-color: var(--primary); color: var(--primary-foreground); }

/* Product-status chips reuse .sp-chip; these are the values it doesn't have. */
.sp-chip--new          { background: var(--success-soft); color: var(--success); }
.sp-chip--discontinued { background: var(--muted); color: var(--muted-foreground); text-decoration: line-through; }
.sp-chip--unknown      { background: var(--muted); color: var(--muted-foreground); }

/* --------------------------------------------------------------------------
   12. Feed ingestion — setup steps and the outbound-link affordance.
   -------------------------------------------------------------------------- */

/* Numbered setup instructions. styles.css has no list-style utilities, so the
   counter is drawn by hand rather than relying on a class that isn't there. */
.sp-steps { counter-reset: sp-step; display: flex; flex-direction: column; gap: .5rem; }
.sp-steps li {
  counter-increment: sp-step; position: relative; padding-left: 1.75rem;
  font-size: .8125rem; line-height: 1.5; list-style: none;
}
.sp-steps li::before {
  content: counter(sp-step);
  position: absolute; left: 0; top: .0625rem;
  width: 1.25rem; height: 1.25rem; border-radius: 50%;
  background: var(--primary); color: var(--primary-foreground);
  font-size: .6875rem; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
}

/* "Open their site" — reads as a launch affordance rather than body copy,
   because on a competitor card that link is a verb, not a fact. */
.sp-visit {
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .25rem .5rem; border-radius: .3125rem;
  border: 1px solid var(--border); background: var(--background);
  font-size: .75rem; color: var(--secondary-foreground);
  text-decoration: none; max-width: 100%;
}
.sp-visit:hover { border-color: var(--primary); color: var(--primary); }
.sp-visit i { font-size: .8125rem; opacity: .8; }

/* ==========================================================================
   LAYOUT-1 GAPS
   Two classes the layout-1 markup uses that the precompiled bundle does not
   define. There is no Tailwind build here, so they have to be written out.
   ========================================================================== */

.kt-footer { flex-shrink: 0; }

/* The sidebar accordion bullet. `size-[6px]` is an arbitrary-value utility
   that was never compiled into the bundle, so the ::before it targets would
   collapse to nothing and every sub-item would lose its dot. */
.size-\[6px\] { width: .375rem; height: .375rem; }

/* ==========================================================================
   DEALERS & RETAILERS
   ========================================================================== */

/* --------------------------------------------------------------------------
   The roster table. Dense on purpose: the whole point is comparing a hundred
   accounts, and a comfortable row height means eight fit on screen.
   -------------------------------------------------------------------------- */
.sp-tablewrap { width: 100%; overflow-x: auto; }

.sp-dtable { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.sp-dtable th, .sp-dtable td {
  padding: .5rem .75rem; text-align: left; border-bottom: 1px solid var(--border);
  vertical-align: middle; white-space: nowrap;
}
.sp-dtable thead th {
  font-size: .6875rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .02em; color: var(--muted-foreground);
  background: var(--muted); position: sticky; top: 0; z-index: 1;
}
.sp-dtable tbody tr:hover { background: color-mix(in oklch, var(--accent) 45%, transparent); }
.sp-dtable tbody tr:last-child td { border-bottom: 0; }

.sp-dtable__num    { text-align: right; font-variant-numeric: tabular-nums; }
.sp-dtable__strong { font-weight: 600; color: var(--mono); }
.sp-dtable__name   { font-weight: 500; color: var(--mono); text-decoration: none; }
.sp-dtable__name:hover { color: var(--primary); }

.sp-dtable__sub {
  display: flex; flex-wrap: wrap; gap: .375rem; align-items: center;
  margin-top: .1875rem; font-size: .6875rem; color: var(--muted-foreground);
}

/* A sortable header has to look clickable, or nobody discovers it. */
.sp-dtable__sortable { cursor: pointer; user-select: none; }
.sp-dtable__sortable:hover { color: var(--primary); }
.sp-dtable__sortable i { font-size: .625rem; opacity: .8; }

.sp-dtable__row--muted td { opacity: .6; }

/* Six months without an order. Read as a warning, not as a date. */
.sp-dtable__lapsed { color: var(--destructive); font-weight: 500; }
.sp-dtable__lapsed i { font-size: .6875rem; margin-inline-start: .1875rem; }

/* --------------------------------------------------------------------------
   Tier — a rank, so it reads as a scale rather than as five unrelated tags.
   -------------------------------------------------------------------------- */
.sp-dtier {
  display: inline-flex; align-items: center; padding: .0625rem .375rem;
  border-radius: .25rem; font-size: .6875rem; font-weight: 600;
  letter-spacing: .01em; white-space: nowrap;
}
.sp-dtier--key       { background: var(--primary); color: var(--primary-foreground); }
.sp-dtier--preferred { background: color-mix(in oklch, var(--primary) 18%, transparent); color: var(--primary); }
.sp-dtier--standard  { background: var(--muted); color: var(--muted-foreground); }
.sp-dtier--probation { background: var(--warning-soft); color: var(--warning); }
.sp-dtier--prospect  { background: var(--info-soft); color: var(--info); }

/* --------------------------------------------------------------------------
   Margin and change. Both carry a judgement, so both carry colour — but only
   these two, so the colour still means something on a dense row.
   -------------------------------------------------------------------------- */
.sp-margin { font-weight: 600; font-variant-numeric: tabular-nums; }
.sp-margin--ok   { color: var(--foreground); }
.sp-margin--warn { color: var(--warning); }
.sp-margin--bad  { color: var(--destructive); }

.sp-delta {
  display: inline-flex; align-items: center; gap: .1875rem;
  font-size: .75rem; font-weight: 500; font-variant-numeric: tabular-nums;
}
.sp-delta i { font-size: .625rem; }
.sp-delta--up   { color: var(--success); }
.sp-delta--down { color: var(--destructive); }
.sp-delta--flat { color: var(--muted-foreground); }
.sp-delta--new  {
  background: var(--info-soft); color: var(--info);
  padding: .0625rem .3125rem; border-radius: .25rem; font-size: .6875rem;
}

/* --------------------------------------------------------------------------
   Sparkline. Zero-anchored, so a flat year looks flat — see dealer-ui.js.
   -------------------------------------------------------------------------- */
.sp-spark { display: block; overflow: visible; }
.sp-spark__line { fill: none; stroke-width: 1.25; stroke-linejoin: round; stroke-linecap: round; }
.sp-spark__area { stroke: none; opacity: .16; }
.sp-spark__dot  { stroke: none; }

/* Stroke and fill are set on SEPARATE selectors on purpose. Grouping the line
   with the dot — `.sp-spark--up .sp-spark__line, .sp-spark--up .sp-spark__dot
   { stroke: X; fill: X }` — silently repaints the line, because that selector
   outranks the `fill: none` above it and SVG implicitly closes an open path in
   order to fill it. The result is a solid blob, with no error anywhere. */
.sp-spark--up   .sp-spark__line { stroke: var(--success); }
.sp-spark--up   .sp-spark__dot  { fill: var(--success); }
.sp-spark--up   .sp-spark__area { fill: var(--success); }
.sp-spark--down .sp-spark__line { stroke: var(--destructive); }
.sp-spark--down .sp-spark__dot  { fill: var(--destructive); }
.sp-spark--down .sp-spark__area { fill: var(--destructive); }
.sp-spark--flat .sp-spark__line { stroke: var(--muted-foreground); }
.sp-spark--flat .sp-spark__dot  { fill: var(--muted-foreground); }
.sp-spark--flat .sp-spark__area { fill: var(--muted-foreground); }

.sp-spark--empty {
  display: inline-block; height: 1.5rem; border-bottom: 1px dashed var(--border);
}

/* --------------------------------------------------------------------------
   Monthly bars. Season months (Jul–Sep) are tinted, because without them a
   marching-band sales chart reads as volatility rather than as a season.
   -------------------------------------------------------------------------- */
.sp-bars {
  display: flex; align-items: flex-end; gap: .1875rem;
  height: 9rem; padding-top: .5rem;
}
.sp-bars__col {
  flex: 1 1 0; min-width: .5rem; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: stretch;
}
.sp-bars__bar {
  background: color-mix(in oklch, var(--primary) 55%, transparent);
  border-radius: .1875rem .1875rem 0 0; min-height: 2px;
  transition: background .15s;
}
.sp-bars__col:hover .sp-bars__bar { background: var(--primary); }
.sp-bars__col--season .sp-bars__bar { background: var(--primary); }
.sp-bars__col--season:hover .sp-bars__bar { background: color-mix(in oklch, var(--primary) 80%, var(--foreground)); }
.sp-bars__tick {
  font-size: .5625rem; color: var(--muted-foreground); text-align: center;
  margin-top: .25rem; font-variant-numeric: tabular-nums;
}
.sp-bars__col--season .sp-bars__tick { color: var(--primary); font-weight: 600; }

/* --------------------------------------------------------------------------
   Definition list — profile facts, two columns on wide cards.
   -------------------------------------------------------------------------- */
.sp-deflist { display: flex; flex-direction: column; gap: .375rem; }
.sp-deflist > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; font-size: .8125rem;
}
.sp-deflist dt { color: var(--muted-foreground); flex-shrink: 0; }
.sp-deflist dd { color: var(--foreground); text-align: right; margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   Where a figure came from. Imported and hand-entered rows behave differently
   on the next import, so they have to be told apart at a glance.
   -------------------------------------------------------------------------- */
.sp-src-tag {
  display: inline-flex; padding: .0625rem .3125rem; border-radius: .25rem;
  font-size: .625rem; font-weight: 500; text-transform: uppercase; letter-spacing: .02em;
}
.sp-src-tag--import { background: var(--muted); color: var(--muted-foreground); }
.sp-src-tag--manual { background: var(--info-soft); color: var(--info); }
.sp-src-tag--api    { background: var(--violet-soft); color: var(--violet); }

/* Arming a destructive button on first click — see removePeriod(). */
.sp-danger i { color: var(--destructive); }

/* ==========================================================================
   IMPORT
   ========================================================================== */

.sp-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .375rem; padding: 2rem 1rem; text-align: center;
  border: 2px dashed var(--border); border-radius: .625rem;
  background: color-mix(in oklch, var(--muted) 40%, transparent);
  transition: border-color .15s, background .15s;
}
.sp-drop i { font-size: 1.75rem; color: var(--muted-foreground); }
.sp-drop--over   { border-color: var(--primary); background: color-mix(in oklch, var(--primary) 7%, transparent); }
.sp-drop--loaded { border-style: solid; border-color: var(--primary); }
.sp-drop--loaded i { color: var(--primary); }

.sp-tile {
  padding: .75rem; border: 1px solid var(--border); border-radius: .5rem;
  background: var(--background);
}
.sp-tile__value { font-size: 1.25rem; font-weight: 600; color: var(--mono); line-height: 1.2; }
.sp-tile__label { font-size: .6875rem; color: var(--muted-foreground); margin-top: .125rem; }
.sp-tile--warning { border-color: var(--warning); }
.sp-tile--warning .sp-tile__value { color: var(--warning); }

.sp-notice {
  display: flex; gap: .625rem; padding: .75rem .875rem; border-radius: .5rem;
  font-size: .8125rem; line-height: 1.45;
}
.sp-notice i { font-size: 1rem; flex-shrink: 0; margin-top: .0625rem; }
.sp-notice ul { margin: 0; padding-inline-start: 1rem; list-style: disc; }
.sp-notice--warn { background: var(--warning-soft); color: var(--warning); }
.sp-notice--bad  { background: color-mix(in oklch, var(--destructive) 10%, transparent); color: var(--destructive); }

.sp-details > summary {
  cursor: pointer; font-size: .8125rem; font-weight: 500; color: var(--secondary-foreground);
  padding: .375rem 0; list-style: none;
}
.sp-details > summary::-webkit-details-marker { display: none; }
.sp-details > summary::before {
  content: '▸'; display: inline-block; margin-inline-end: .375rem;
  color: var(--muted-foreground); transition: transform .15s;
}
.sp-details[open] > summary::before { transform: rotate(90deg); }
.sp-details > summary:hover { color: var(--primary); }
.sp-details__warn { color: var(--warning); }

/* ==========================================================================
   HEADER SEARCH — the jump bar
   ========================================================================== */

.sp-jump {
  display: flex; align-items: center; gap: .625rem;
  padding: .5rem .875rem; text-decoration: none; color: inherit;
}
.sp-jump:hover { background: var(--accent); }
.sp-jump i { font-size: .875rem; color: var(--muted-foreground); flex-shrink: 0; }
.sp-jump__label { display: block; font-size: .8125rem; font-weight: 500; color: var(--foreground); }
.sp-jump__hint  { display: block; font-size: .6875rem; color: var(--muted-foreground); }
.sp-jump__group {
  padding: .5rem .875rem .25rem; font-size: .625rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; color: var(--muted-foreground);
}

/* The search modal is anchored near the top rather than centred: it opens on
   a keystroke, and a box that jumps to the middle of the screen reads as an
   interruption instead of a tool. */
#sp_search_modal .sp-modal__panel { margin-top: 8vh; align-self: flex-start; }
#sp_search_modal .kt-input:focus { outline: none; box-shadow: none; }


/* ==========================================================================
   MARKET INTELLIGENCE — band-density choropleth
   Public NCES data only; see app/pages/market-intel.js for why that matters.
   ========================================================================== */

.sp-mapwrap { position: relative; width: 100%; }
.sp-map { display: block; width: 100%; height: auto; }
.sp-map path { transition: opacity .12s; }
.sp-map path:hover { opacity: .82; cursor: default; }

/* Follows the cursor, so it must not intercept the mousemove that positions it. */
.sp-maptip {
  position: absolute; z-index: 3; pointer-events: none;
  background: var(--mono); color: var(--background);
  padding: .5rem .625rem; border-radius: .375rem;
  font-size: .75rem; line-height: 1.5; white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0,0,0,.18);
}
.sp-maptip[hidden] { display: none; }

.sp-ramp-row { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-top: .875rem; }
.sp-ramp {
  display: flex; border-radius: .3125rem; overflow: hidden;
  border: 1px solid var(--border);
}
.sp-ramp i { display: block; width: 1.625rem; height: .6875rem; }

/* The density figure is coloured from the same ramp as its state, so the table and the
   map agree without the reader having to match a number to a shade by eye. */
.sp-density { font-weight: 600; font-variant-numeric: tabular-nums; }

/* One school with a band program per dot. Near-black at low opacity so 13,922 of them read
   as texture over the choropleth rather than fighting it for the same colour space. */
.sp-schooldots { fill: #10151B; opacity: .42; stroke: none; pointer-events: none; }
.dark .sp-schooldots { fill: #F2F6FA; opacity: .40; }


/* Market Intelligence — Organizations / Regional Associations */
.sp-chips { display: flex; flex-wrap: wrap; gap: .375rem; }

/* A state with an inline share bar: the code and the count read at a glance, the bar gives
   the shape without a second chart. */
.sp-statechip {
  display: inline-flex; align-items: center; gap: .4375rem;
  padding: .1875rem .5rem; border-radius: .3125rem;
  border: 1px solid var(--border); background: var(--background);
  font-size: .75rem; color: var(--secondary-foreground);
}
.sp-statechip b { color: var(--mono); font-weight: 600; }
.sp-statechip__bar {
  display: block; width: 2.5rem; height: .3125rem; border-radius: 999px;
  background: var(--muted); overflow: hidden;
}
.sp-statechip__bar i { display: block; height: 100%; border-radius: 999px; }


/* Score bar beside a corps name: the spread from 20th to 1st is only ~19 points, so the bar
   is scaled to the RANGE rather than to zero — anchored at zero every corps looks identical. */
.sp-scorebar {
  display: inline-block; width: 3.25rem; height: .25rem; margin-inline-start: .5rem;
  border-radius: 999px; background: var(--muted); overflow: hidden; vertical-align: middle;
}
.sp-scorebar i { display: block; height: 100%; background: var(--violet); border-radius: 999px; }

/* Organizations page — two independent columns instead of a stretching grid.
   ⚠️ In a plain 2-col grid every card in a row is inflated to the tallest, so the 50-row
   USBands card padded DCI out with dead space. Here each column is its own flex stack, so a
   card is only ever as tall as its own content.
   ⚠️ The DOM is column-major (cards 1,3 then 2,4). Cards carry an inline `order`, and below
   the lg breakpoint the columns go `display:contents` so the single-column stack still reads
   1,2,3,4 rather than the column-major order. */
.sp-orgcols { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
.sp-orgcol { display: contents; }
@media (min-width: 1024px) {
  .sp-orgcols { grid-template-columns: 1fr 1fr; align-items: start; }
  .sp-orgcol { display: flex; flex-direction: column; gap: 1.25rem; }
}

/* --------------------------------------------------------------------------
   Drill-down modal — the clickable breakdown bars on Organizations.

   ⚠️ NOT named .sp-modal — that class already exists above and is shared by the header
   search dialog and five other pages (intel, dealers, dealer, competitors, competitor).
   Redefining it here silently restyled all of them. Own prefix, own rules.
   Hand-rolled rather than KTModal on purpose: this project ships a PRECOMPILED Tailwind
   bundle, so any Metronic class not already in styles.css silently does nothing. Owning
   these rules here is what lets `npm run audit:css` actually verify them.
   -------------------------------------------------------------------------- */
.sp-drill {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  padding: 0; margin: 0 0 .625rem; cursor: pointer; border-radius: .375rem;
}
.sp-drill:hover .sp-drill__label { color: var(--mono); }
.sp-drill:hover .sp-progress { background: color-mix(in oklch, var(--muted) 60%, transparent); }
.sp-drill:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.sp-drill__caret { font-size: .625rem; opacity: .55; margin-inline-start: .3rem; }

.sp-dmodal[hidden] { display: none; }
.sp-dmodal {
  position: fixed; inset: 0; z-index: 120; display: flex;
  align-items: center; justify-content: center; padding: 1.5rem;
}
.sp-dmodal__scrim { position: absolute; inset: 0; background: rgb(0 0 0 / .5); }
.sp-dmodal__panel {
  position: relative; display: flex; flex-direction: column;
  width: min(46rem, 100%); max-height: min(44rem, 88vh);
  background: var(--background); border: 1px solid var(--border);
  border-radius: .75rem; box-shadow: 0 24px 60px rgb(0 0 0 / .28); overflow: hidden;
}
.sp-dmodal__head {
  display: flex; align-items: flex-start; gap: 1rem;
  padding: 1.15rem 1.25rem .95rem; border-bottom: 1px solid var(--border);
}
.sp-dmodal__title { font-size: .9375rem; font-weight: 600; color: var(--mono); }
.sp-dmodal__note {
  font-size: .8125rem; line-height: 1.5; color: var(--secondary-foreground); margin-top: .4rem;
}
.sp-dmodal__x {
  flex: none; width: 1.75rem; height: 1.75rem; display: grid; place-items: center;
  border: 0; border-radius: .375rem; background: none; cursor: pointer;
  color: var(--muted-foreground); font-size: .875rem;
}
.sp-dmodal__x:hover { background: var(--muted); color: var(--mono); }
.sp-dmodal__tools {
  display: flex; gap: .625rem; flex-wrap: wrap;
  padding: .8rem 1.25rem; border-bottom: 1px solid var(--border);
}
.sp-dmodal__body { flex: 1 1 auto; overflow: auto; }
.sp-dmodal__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .8rem 1.25rem; border-top: 1px solid var(--border);
  font-size: .8125rem; color: var(--muted-foreground);
}
.sp-field {
  font-size: .8125rem; padding: .4rem .6rem; border-radius: .375rem;
  border: 1px solid var(--input); background: var(--background); color: var(--foreground);
}
.sp-field:focus-visible { outline: 2px solid var(--primary); outline-offset: -1px; }
.sp-field--grow { flex: 1 1 12rem; min-width: 0; }
.sp-sort {
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  color: inherit; text-transform: inherit; letter-spacing: inherit;
}
.sp-sort:hover { color: var(--mono); }
.sp-sort__dir { margin-inline-start: .25rem; opacity: .5; }
.sp-dmodal__empty { padding: 2.5rem 1.25rem; text-align: center; color: var(--muted-foreground); }

/* Governance legend key on the market map — a swatch plus its label. */
.sp-govkey { display: inline-flex; align-items: center; gap: .35rem;
  font-size: .75rem; color: var(--muted-foreground); margin-inline-end: .75rem; }
.sp-govkey i { width: .75rem; height: .75rem; border-radius: .1875rem; display: block; }
/* Dimmed line inside the map tooltip — the tooltip has its own dark ground, so the page's
   muted-foreground token does not read against it. */
.sp-tipdim { opacity: .7; }

/* ---------------------------------------------------------------- product levels
   StylePLUS stock by size beside what American Band carries. The `sp-plv` prefix was
   confirmed unused before this block was added — `audit:css` catches a MISSING class,
   never a DUPLICATED one, so a reused prefix would silently restyle another page. */

.sp-plv__stats {
  display: grid; gap: .75rem; margin-bottom: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.sp-plv__stat {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .875rem 1rem;
}
.sp-plv__stat-value {
  font-size: 1.375rem; font-weight: 600; color: var(--mono);
  font-variant-numeric: tabular-nums; line-height: 1.2;
}
.sp-plv__stat-label {
  font-size: .6875rem; text-transform: uppercase; letter-spacing: .02em;
  color: var(--muted-foreground); margin-top: .125rem;
}

.sp-plv__meta { font-size: .75rem; color: var(--muted-foreground); }

/* Column widths held steady so the two quantity columns line up as rows expand. */
.sp-plv__table th:nth-child(1), .sp-plv__table td:nth-child(1) { min-width: 15rem; }
.sp-plv__table th:nth-child(2), .sp-plv__table td:nth-child(2) { width: 100%; }
.sp-plv__table th:nth-child(3), .sp-plv__table td:nth-child(3),
.sp-plv__table th:nth-child(4), .sp-plv__table td:nth-child(4) { min-width: 11rem; }
/* The head row now carries two lines in the totals cells. */
.sp-plv__head > td { vertical-align: top; padding-top: .625rem; }

/* The table is tall and wide; cap it so the page scrolls the body, not the window. */
.sp-plv__scroll { max-height: 70vh; overflow: auto; }
/* Phones: the desktop minimums add up to ~850px, so tighten them; whatever is still wider
   scrolls inside .sp-plv__scroll, never the page. */
@media (max-width: 40rem) {
  .sp-plv__table th:nth-child(1), .sp-plv__table td:nth-child(1) { min-width: 10rem; }
  .sp-plv__table th:nth-child(3), .sp-plv__table td:nth-child(3),
  .sp-plv__table th:nth-child(4), .sp-plv__table td:nth-child(4) { min-width: 6.5rem; }
}

.sp-plv__head { cursor: pointer; }
.sp-plv__head > td { border-top: 1px solid var(--border); }
.sp-plv__head:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

.sp-plv__caret {
  display: inline-flex; width: 1rem; color: var(--muted-foreground);
  transition: transform .15s ease; margin-inline-end: .25rem;
}
.sp-plv__caret i { font-size: .625rem; }
.sp-plv__head.is-open .sp-plv__caret { transform: rotate(90deg); }

.sp-plv__kid > td:first-child {
  padding-inline-start: 2rem; color: var(--muted-foreground);
  font-size: .75rem; font-variant-numeric: tabular-nums;
}
.sp-plv__kid { background: color-mix(in oklch, var(--muted) 30%, transparent); }

.sp-plv__dim  { color: var(--muted-foreground); }
.sp-plv__zero { color: var(--muted-foreground); opacity: .55; }
.sp-plv__carried { color: var(--muted-foreground); }
.sp-plv__notcarried {
  color: var(--muted-foreground); font-style: italic; opacity: .8;
}


/* Search: the match count and the "also on Flags (3)" links sit beside the box. */
.sp-plv__found { font-size: .8125rem; color: var(--muted-foreground); }

/* Allowlist status on each product row — "is this one syncing?" answered without a click. */
.sp-plv__syncing, .sp-plv__nosync {
  display: inline-block; margin-inline-start: .375rem; padding: .0625rem .375rem;
  border-radius: calc(var(--radius) - 3px); font-size: .625rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .02em; white-space: nowrap; vertical-align: 1px;
}
.sp-plv__syncing { background: var(--success-soft); color: var(--success); }
.sp-plv__nosync  { background: var(--muted); color: var(--muted-foreground); }

/* A negative ABA count is an oversell, not a rounding artefact — it must read as a
   warning, never be dimmed like an ordinary zero. */
.sp-plv__negative { color: var(--destructive); font-weight: 600; }

.sp-plv__flag {
  display: inline-block; margin-inline-start: .375rem; padding: .0625rem .3125rem;
  border-radius: calc(var(--radius) - 3px); font-size: .625rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: .02em; white-space: nowrap;
  background: color-mix(in oklch, var(--warning) 18%, transparent);
  color: color-mix(in oklch, var(--warning) 75%, var(--mono));
}

/* The gap against StylePLUS. Quiet by default — most rows differ by a unit or two and
   that is not worth shouting about; only a material gap takes the warning colour. */
.sp-plv__delta {
  display: inline-block; margin-inline-start: .375rem;
  font-size: .6875rem; font-weight: 500; font-variant-numeric: tabular-nums;
  color: var(--muted-foreground);
}
.sp-plv__delta--big { color: var(--destructive); font-weight: 600; }

/* A product total spreads over dozens of sizes; the spread sits under the number so a
   deep pocket of stock in one size cannot pass for even distribution. */
.sp-plv__spread {
  font-size: .625rem; font-weight: 400; color: var(--muted-foreground);
  margin-top: .125rem; white-space: nowrap; letter-spacing: .01em;
}

/* Freshness of the ABA column. Neutral while checking, quiet green when live, and
   explicitly marked when the live check failed and the snapshot is what you are seeing. */
.sp-plv__live {
  display: inline-block; padding: .0625rem .375rem; border-radius: calc(var(--radius) - 3px);
  font-size: .625rem; font-weight: 500; white-space: nowrap;
  background: var(--muted); color: var(--muted-foreground);
}
.sp-plv__live--ok {
  background: color-mix(in oklch, var(--primary) 14%, transparent);
  color: color-mix(in oklch, var(--primary) 80%, var(--mono));
}
.sp-plv__live--stale {
  background: color-mix(in oklch, var(--warning) 18%, transparent);
  color: color-mix(in oklch, var(--warning) 78%, var(--mono));
}

/* ------------------------------------------------------------------ sync controls
   "Check" is safe; "Sync now" writes to a live storefront, so it arms on the first
   click and only writes on the second. */
.sp-sync { margin-bottom: 1rem; }
.sp-sync__bar {
  display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; justify-content: space-between;
}
.sp-sync__blurb { font-size: .8125rem; line-height: 1.4; }
.sp-sync__blurb div { color: var(--muted-foreground); font-size: .75rem; }
.sp-sync__actions { display: flex; gap: .5rem; flex-wrap: wrap; }
.sp-sync__out { border-top: 1px solid var(--border); padding: .875rem 1.25rem 1rem; }
.sp-sync__status { font-size: .8125rem; line-height: 1.5; }
.sp-sync__status--bad { color: var(--destructive); }
.sp-sync__table { margin-top: .625rem; }
.sp-sync__skips { margin-top: .625rem; font-size: .75rem; color: var(--muted-foreground); }
.sp-sync__skips summary { cursor: pointer; }
.sp-sync__skips div { padding: .125rem 0 .125rem .875rem; }

/* ---------------------------------------------------------------- flag groups
   Flags need a third level — group → product → variants — because 872 products is not a
   browsable list. The group row reads as a section heading, not as another product. */
.sp-plv__group { cursor: pointer; background: var(--muted); }
.sp-plv__group > td {
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding-top: .5rem; padding-bottom: .5rem;
}
.sp-plv__group:hover { background: color-mix(in oklch, var(--accent) 55%, var(--muted)); }
.sp-plv__group:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.sp-plv__group-name {
  font-weight: 600; color: var(--mono); font-size: .8125rem;
  text-transform: uppercase; letter-spacing: .02em;
}
.sp-plv__group-meta {
  margin-inline-start: .625rem; font-size: .6875rem; color: var(--muted-foreground);
  text-transform: none; letter-spacing: 0;
}
.sp-plv__group-num { font-weight: 600; color: var(--mono); }
/* The group caret points down when open; the product caret rotates via .is-open. */
.sp-plv__caret--open { transform: rotate(90deg); }

/* ===========================================================================
   Product research (Intel Feed).

   Own namespace on purpose. `.sp-res` was checked as unused before a line of
   it was written — app.css has no build step and no duplicate-selector check,
   so redefining an existing name silently restyles every page that uses it.
   =========================================================================== */

.sp-res__ask-label {
  display: block; font-size: .8125rem; font-weight: 600;
  color: var(--secondary-foreground); margin-bottom: .5rem;
}
.sp-res__ask-row { display: flex; gap: .625rem; flex-wrap: wrap; }
.sp-res__ask-input { flex: 1 1 18rem; min-width: 0; }
.sp-res__ask-hint {
  margin-top: .5rem; font-size: .6875rem; color: var(--muted-foreground); line-height: 1.5;
}

/* Section header doubles as the expand control, so it has to look pressable. */
.sp-res__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; cursor: pointer; user-select: none;
}
.sp-res__head:hover { background: color-mix(in oklch, var(--accent) 40%, transparent); }
.sp-res__head-main { display: flex; align-items: center; gap: .625rem; min-width: 0; }
.sp-res__head-meta { display: flex; align-items: center; gap: .375rem; flex-shrink: 0; }
.sp-res__added { font-size: .6875rem; color: var(--muted-foreground); white-space: nowrap; }

.sp-res__caret {
  font-size: .75rem; color: var(--muted-foreground);
  transition: transform .15s; transform: rotate(-90deg);
}
.sp-res__caret--open { transform: rotate(0deg); }

.sp-res__loading {
  display: flex; align-items: center; gap: .5rem; padding: 2rem 0;
  justify-content: center; font-size: .8125rem; color: var(--muted-foreground);
}

/* The denominator that travels with every count. Deliberately not muted into
   invisibility — "of 9 scanned" is the part that keeps the number honest. */
.sp-res__denom {
  display: block; font-size: .625rem; color: var(--muted-foreground);
  opacity: .85; margin-top: .0625rem;
}
.sp-res__tile-where { display: block; margin-top: .0625rem; color: var(--primary); }
.sp-res__tile-where i { font-size: .625rem; }
.sp-res__muted { color: var(--muted-foreground); font-weight: 400; }
.sp-res__scope { align-items: flex-start; line-height: 1.5; display: flex; }

.sp-res__panel { padding-top: 1.25rem; border-top: 1px solid var(--border); margin-top: 1.25rem; }
.sp-res__panel:first-child { padding-top: 0; border-top: 0; margin-top: 0; }
.sp-res__panel-head {
  display: flex; align-items: center; gap: .5rem; margin-bottom: .75rem;
  font-size: .875rem; font-weight: 600; color: var(--mono);
}
.sp-res__panel-head i { color: var(--muted-foreground); font-size: .9375rem; }
.sp-res__subhead {
  font-size: .75rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .02em; color: var(--muted-foreground); margin: 1rem 0 .5rem;
}
.sp-res__note {
  font-size: .75rem; color: var(--muted-foreground);
  line-height: 1.5; margin-bottom: .75rem;
}

.sp-res__table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.sp-res__table th, .sp-res__table td {
  padding: .5rem .625rem; text-align: left;
  border-bottom: 1px solid var(--border); vertical-align: top;
}
.sp-res__table thead th {
  font-size: .6875rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .02em; color: var(--muted-foreground); white-space: nowrap;
}
.sp-res__table tbody tr:hover { background: color-mix(in oklch, var(--accent) 40%, transparent); }
.sp-res__table a { color: var(--primary); }
.sp-res__num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sp-res__store { font-weight: 500; white-space: nowrap; }
.sp-res__stores { font-size: .6875rem; color: var(--muted-foreground); line-height: 1.4; }

/* A delisted row stays visible rather than being dropped: "it used to be here"
   is information, and a silently shorter table is not. */
.sp-res__gone { opacity: .5; }
.sp-res__gonelist {
  margin: 0 0 .75rem; padding-inline-start: 1rem; list-style: disc;
  font-size: .75rem; color: var(--secondary-foreground); line-height: 1.7;
}

.sp-res__hist { display: flex; gap: .5rem; flex-wrap: wrap; }
.sp-res__hist-day {
  padding: .5rem .625rem; border: 1px solid var(--border);
  border-radius: .375rem; background: var(--background); min-width: 6.5rem;
}
.sp-res__hist-date { font-size: .625rem; color: var(--muted-foreground); text-transform: uppercase; }
.sp-res__hist-range {
  font-size: .8125rem; font-weight: 600; color: var(--mono);
  font-variant-numeric: tabular-nums; margin-top: .125rem;
}

/* "Nothing collected" is a state with its own look, so it can never be
   mistaken for "we looked and found nothing". */
.sp-res__pending {
  display: flex; gap: .75rem; padding: 1rem;
  border: 1px dashed var(--border); border-radius: .5rem;
  background: var(--muted); font-size: .8125rem; margin-bottom: .75rem;
}
.sp-res__pending i { font-size: 1.125rem; color: var(--muted-foreground); flex-shrink: 0; }
.sp-res__pending .sp-res__muted { font-size: .75rem; line-height: 1.5; margin-top: .25rem; }

.sp-res__probes { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.sp-res__probes-label { font-size: .6875rem; color: var(--muted-foreground); }

.sp-res__findings { list-style: none; margin: 0 0 .875rem; padding: 0; }
.sp-res__finding { padding: .75rem 0; border-bottom: 1px solid var(--border); }
.sp-res__finding:last-child { border-bottom: 0; }
.sp-res__finding-head {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  font-size: .6875rem; margin-bottom: .25rem;
}
.sp-res__finding-title { font-size: .8125rem; font-weight: 500; line-height: 1.4; }
.sp-res__finding-title a { color: var(--primary); }
.sp-res__finding-body {
  font-size: .75rem; color: var(--secondary-foreground);
  line-height: 1.5; margin: .25rem 0; white-space: pre-wrap;
}

.sp-res__terms { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; }
.sp-res__terms .sp-field { flex: 1 1 12rem; min-width: 0; }

@media (max-width: 640px) {
  .sp-res__head { flex-wrap: wrap; }
  .sp-res__terms .sp-field { flex-basis: 100%; }
}

/* Model table — the answer row. Heavier than the evidence rows below it. */
.sp-res__model { font-weight: 500; }
.sp-res__model a { color: var(--primary); }
.sp-res__buy { white-space: nowrap; font-size: .75rem; }
.sp-res__buy a { color: var(--primary); font-weight: 500; }
.sp-res__buy i { font-size: .625rem; }
.sp-res__buy .sp-res__muted { margin-inline-start: .375rem; font-variant-numeric: tabular-nums; }
.sp-res__evidence { margin-top: 1rem; }
.sp-res__evidence .sp-res__note { margin-top: .5rem; }

/* Equipment page. `sp-eq` was verified unused before a line of it was written —
   app.css has no build step and no duplicate-selector check. */
.sp-eq__bar { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; }
.sp-eq__bar .sp-field { flex: 0 1 14rem; }
.sp-eq__bar .sp-field--grow { flex: 1 1 16rem; }
.sp-eq__check {
  display: flex; align-items: center; gap: .375rem;
  font-size: .8125rem; color: var(--secondary-foreground);
  padding-bottom: .5rem; white-space: nowrap; cursor: pointer;
}
.sp-eq__warn { margin-top: .625rem; align-items: flex-start; line-height: 1.5; }

@media (max-width: 640px) {
  .sp-eq__bar .sp-field, .sp-eq__bar .sp-field--grow { flex-basis: 100%; }
}

/* --- 100% stacked split bar, for share-of-dollars vs share-of-units ---------
   ⚠️ NOT .sp-bars — that name is already taken by the vertical column chart
   (.sp-bars__col/.sp-bars__tick). Redefining it would silently restyle every page
   using that component. Checked before adding; these five names were free. */
.sp-splitbar { margin-top: .75rem; }
.sp-splitbar__row { display: flex; align-items: center; gap: .875rem; margin: .5rem 0; }
.sp-splitbar__label { width: 4.25rem; text-align: right; font-size: .6875rem;
  letter-spacing: .05em; font-weight: 700; color: var(--primary); }
.sp-splitbar__track { flex: 1; display: flex; height: 1.75rem; border-radius: .25rem;
  overflow: hidden; }
.sp-splitbar__track > div { display: flex; align-items: center; justify-content: center;
  font-size: .75rem; font-weight: 700; color: #fff; }
.sp-splitbar__track > div:last-child { color: var(--muted-foreground); }
.sp-splitkey { margin-top: .625rem; margin-left: 5.125rem; display: flex; gap: 1rem;
  flex-wrap: wrap; font-size: .75rem; color: var(--muted-foreground); }
.sp-splitkey span { display: inline-flex; align-items: center; gap: .35rem; }
.sp-splitkey i { width: .75rem; height: .75rem; border-radius: .1875rem; display: block; }

/* A row carrying more weight than its neighbours, without shouting. */
.sp-row--hi > td { background: color-mix(in oklch, var(--primary) 6%, transparent); }

/* --- sponsorship pages ------------------------------------------------------
   Names checked free before adding (audit:css finds MISSING classes, never
   DUPLICATED ones, so a collision here would silently restyle another page). */
.sp-subhead { font-size: .6875rem; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; color: var(--primary); margin: 1.25rem 0 .5rem; }
.sp-subhead:first-of-type { margin-top: 0; }

/* A stated gap in the data. Deliberately looks unfinished rather than neutral —
   a missing number should read as missing, not as zero. */
.sp-gap { border: 1px dashed var(--border); border-radius: .375rem; padding: .75rem .875rem;
  background: color-mix(in oklch, var(--warning) 6%, transparent); }
.sp-gap__label { font-size: .75rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--warning); margin-bottom: .25rem; }

/* Prose cell inside an sp-dtable.
   ⚠️ .sp-dtable td sets white-space:nowrap, which is correct for the numeric tables it was
   built for and wrong for a cell carrying a sentence — the sponsorship tables blew the page
   out to 2,300px before this existed. A MODIFIER rather than a change to the shared rule,
   which would have silently re-laid-out every table in the app. */
/* ⚠️ Must be `.sp-dtable td.sp-dtable__wrap`, not `.sp-dtable__wrap` alone. The rule it
   overrides is `.sp-dtable th, .sp-dtable td` at (0,1,1); a lone class is (0,1,0) and
   LOSES, so the first version of this modifier did nothing at all and the tables still
   blew past the viewport. */
.sp-dtable td.sp-dtable__wrap { white-space: normal; overflow-wrap: anywhere; min-width: 12rem; }

/* Competitive placement badge, shown beside a sponsored group's name.
   Deliberately NOT an .sp-chip modifier: a chip carries STATUS (something we set), and a
   placement is an outside fact we only report. Keeping them visually distinct stops the DCI
   table reading as though "1st" were a sponsorship tier.

   ⚠️ NEUTRAL ON PURPOSE. The obvious version — brand green on a 12% tint of itself — measures
   3.90:1, under AA for 11px text, and `--primary-soft` is referenced by the other chips but
   never actually DEFINED. --foreground on --muted is 18.1:1 light / 17.0:1 dark.
   ⚠️ Do not "fix" the unranked variant to --muted-foreground: zinc-500 on --muted is 4.40:1
   light and 3.67:1 dark, both under AA. (The older .sp-chip--planning has that problem; it is
   pre-existing and not being widened here.) */
.sp-rank {
  display: inline-block; margin-inline-start: .375rem;
  padding: .0625rem .375rem; border-radius: .25rem;
  font-size: .6875rem; font-weight: 600; line-height: 1rem;
  font-variant-numeric: tabular-nums; white-space: nowrap; vertical-align: middle;
  background: var(--muted); color: var(--foreground);
}
/* No pill and a lighter weight, so "outside top 20" reads as the absence of a placement
   rather than as another badge. */
.sp-rank--none {
  background: transparent; padding-inline: 0;
  color: var(--secondary-foreground); font-weight: 500;
}

/* --------------------------------------------------------------------------
   Calendar — the trading year as a 12-month grid.

   ⚠️ ACCENTS ARE DERIVED WITH color-mix FROM A SINGLE -600 PER HUE. The compiled
   Metronic bundle only contains the shades its demos happened to use: indigo and
   emerald are absent ENTIRELY, and teal has only -600. Writing var(--color-teal-50)
   here would silently resolve to nothing. Mixing from the one shade that is known
   to exist is what makes these safe.
   ⚠️ Bar text is --foreground on a 14% tint, never the accent on its own tint —
   the accent-on-tint version measures under 4:1 for every hue here.
   -------------------------------------------------------------------------- */
.sp-cal__dot,
.sp-cal__bar { --accent: var(--color-zinc-600); }
[data-cal-accent="zinc"]   { --accent: var(--color-zinc-600); }
[data-cal-accent="violet"] { --accent: var(--color-violet-600); }
[data-cal-accent="blue"]   { --accent: var(--color-blue-600); }
[data-cal-accent="teal"]   { --accent: var(--color-teal-600); }
[data-cal-accent="green"]  { --accent: var(--color-green-600); }
[data-cal-accent="yellow"] { --accent: var(--color-yellow-600); }
[data-cal-accent="olive"]  { --accent: var(--primary); }

.sp-cal__dot {
  display: inline-block; width: .5rem; height: .5rem; border-radius: 9999px;
  background: var(--accent); flex: none;
}
.sp-cal__dot--derived {
  background: transparent; border: 1px dashed var(--muted-foreground);
  width: .625rem; height: .625rem;
}

/* ---- toolbar + legend ---- */
.sp-cal__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem; }
.sp-cal__views { display: flex; gap: .5rem; flex: none; }
.sp-cal__legend { display: flex; flex-wrap: wrap; gap: .375rem 1rem; }
.sp-cal__legenditem {
  display: inline-flex; align-items: center; gap: .375rem;
  font-size: .75rem; color: var(--secondary-foreground);
}

/* ---- "where we are now" ---- */
.sp-cal__now { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 64rem) { .sp-cal__now { grid-template-columns: 1fr 1fr; } }
/* ⚠️ min-width:0 is load-bearing, not tidiness. A grid child defaults to min-width:auto,
   so a long bar label refuses to shrink and widens the whole card — the same failure the
   shell's five min-w-0 wrappers exist to prevent. */
.sp-cal__nowcol { min-width: 0; }
.sp-cal__nowlist { display: flex; flex-direction: column; gap: .625rem; }
.sp-cal__nowlist li { display: flex; align-items: baseline; gap: .5rem; font-size: .875rem; }

/* ---- the grid ----
   The wrapper scrolls, not the page: a 12-column grid cannot fit a phone, and letting it
   widen the shell is the failure mode that already cost a fix once (see the min-w-0 note
   on the shell wrappers). */
.sp-cal__scroll { overflow-x: auto; overflow-y: hidden; margin-bottom: 1.25rem; }
.sp-cal__grid {
  display: grid;
  grid-template-columns: 13rem repeat(12, minmax(3.25rem, 1fr));
  align-items: center;
  min-width: 54rem;
  row-gap: .25rem;
}
.sp-cal__corner { grid-column: 1; }
.sp-cal__month {
  font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted-foreground); text-align: center; padding-bottom: .375rem;
}
.sp-cal__month.is-now { color: var(--primary); }

.sp-cal__lane {
  grid-column: 1; display: flex; align-items: center; gap: .5rem;
  font-size: .75rem; font-weight: 600; color: var(--secondary-foreground);
  padding-inline-end: .75rem; min-height: 1.75rem;
}
/* ⚠️ The gap between lanes is a margin on the FIRST packed row, and all three item types in
   that row must carry it — label, background cell AND bar. Leaving it off the bar offsets
   every bar 8px above the lane it belongs to: subtle enough to look like a rendering quirk
   rather than a bug, and it survived a first round of fixes because the one bar on a
   second packed row ("Advertise") lined up correctly and made the grid look sound. */
.sp-cal__lane.is-first { margin-top: .5rem; }

/* Empty cells only draw the month gridlines; the bars sit in the same grid areas on top. */
.sp-cal__cell { grid-row: auto; height: 1.75rem; border-inline-start: 1px solid var(--border); }
.sp-cal__cell.is-now { background: color-mix(in oklch, var(--primary) 6%, transparent); }
.sp-cal__cell.is-first { margin-top: .5rem; }

.sp-cal__bar {
  grid-row: auto; height: 1.75rem; display: flex; align-items: center;
  padding: 0 .5rem; border-radius: .25rem; overflow: hidden;
  background: color-mix(in oklch, var(--accent) 14%, transparent);
  border-inline-start: 3px solid var(--accent);
  color: var(--foreground); font-size: .6875rem; font-weight: 500; line-height: 1;
}
.sp-cal__bar.is-first { margin-top: .5rem; }
.sp-cal__bar.is-hi { font-weight: 700; box-shadow: inset 0 0 0 1px var(--accent); }
/* Inferred, not briefed — dashed so it can never be mistaken for a stated date. */
.sp-cal__bar.is-derived {
  background: repeating-linear-gradient(135deg,
    color-mix(in oklch, var(--accent) 14%, transparent) 0 6px,
    transparent 6px 12px);
  border-inline-start-style: dashed;
}
/* A run that continues past the edge of this view loses its rounded end, so two segments of
   one window do not read as two separate windows. */
.sp-cal__bar.is-cutl { border-start-start-radius: 0; border-end-start-radius: 0;
  border-inline-start-width: 0; }
.sp-cal__bar.is-cutr { border-start-end-radius: 0; border-end-end-radius: 0; }
.sp-cal__barlabel { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- lead-time chain ---- */
.sp-cal__chain {
  display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; padding: 0; margin: 0;
}
.sp-cal__chainstep {
  flex: 1 1 8rem; min-width: 8rem; padding: .5rem .625rem;
  border: 1px solid var(--border); border-radius: .375rem; background: var(--muted);
}
.sp-cal__chainstep.is-derived { border-style: dashed; background: transparent; }
.sp-cal__chainwhen {
  font-size: .6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--primary);
}
.sp-cal__chainstepname { font-size: .8125rem; color: var(--foreground); }

/* Embedded Box folder. The card content loses its padding so the frame reaches the edges,
   and the frame is sized in JS to the remaining viewport height — a percentage height would
   collapse to zero, because the ancestors have no resolved height to take a percentage of. */
.sp-boxbar { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.sp-boxwrap { padding: 0 !important; }
.sp-boxframe {
  display: block; width: 100%; min-height: 26rem; border: 0;
  border-bottom-left-radius: .625rem; border-bottom-right-radius: .625rem;
  background: var(--muted);
}

/* --------------------------------------------------------------------------
   Project page — the inline-edit checklist.

   ⚠️ `.sp-row` is a NEW base class and `.sp-row--hi` already existed (a table
   row modifier in the sponsorship tables). They never meet: class selectors
   match exactly, so `.sp-row` does not style `.sp-row--hi`. Checked before
   naming, per the rule that cost a day when `.sp-modal` was redefined.
   -------------------------------------------------------------------------- */

/* ---- header ---- */
.sp-phead { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 1rem; justify-content: space-between; }
.sp-phead__name { display: flex; flex-wrap: wrap; align-items: center; gap: .625rem; }
.sp-phead__title { font-size: 1.375rem; font-weight: 600; color: var(--mono); line-height: 1.2; }
.sp-phead__desc { display: block; margin-top: .375rem; font-size: .875rem;
  color: var(--secondary-foreground); max-width: 46rem; }
.sp-phead__right { flex: none; }
.sp-phead__who { display: inline-flex; gap: .25rem; margin-inline-start: .25rem; }

.sp-pmeta { display: grid; gap: .75rem 1.5rem; margin-top: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.sp-pmeta__cell { min-width: 0; }

.sp-pbar { margin-top: 1rem; }
.sp-pbar__legend { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem;
  margin-top: .5rem; font-size: .8125rem; color: var(--secondary-foreground); }

/* Health, derived server-side so every screen agrees. */
.sp-risk { display: inline-block; padding: .125rem .5rem; border-radius: .25rem;
  font-size: .6875rem; font-weight: 600; white-space: nowrap; }
.sp-risk--on_track   { background: var(--success-soft); color: var(--success); }
.sp-risk--at_risk    { background: var(--warning-soft); color: var(--warning); }
.sp-risk--unassigned { background: var(--muted); color: var(--secondary-foreground); }
.sp-risk--blocked    { background: color-mix(in oklch, var(--destructive) 12%, transparent); color: var(--destructive); }
.sp-risk--on_hold,
.sp-risk--archived,
.sp-risk--done       { background: var(--muted); color: var(--secondary-foreground); }

/* ---- click-to-edit ---- */
.sp-edit { cursor: text; border-radius: .25rem; padding: .0625rem .25rem;
  margin-inline-start: -.25rem; transition: background .12s; }
.sp-edit:hover, .sp-edit:focus-visible { background: var(--muted); outline: none; }
.sp-inline { width: 100%; }
.sp-dim { color: var(--muted-foreground); }
.sp-who { display: inline-flex; align-items: center; gap: .375rem; font-size: .875rem; }
.sp-pick { max-width: 14rem; }
.sp-pick--sm { font-size: .8125rem; padding-block: .25rem; }

/* ---- tabs ---- */
.sp-tabs { display: flex; flex-wrap: wrap; gap: .25rem; border-bottom: 1px solid var(--border); }
.sp-tabs__btn { display: inline-flex; align-items: center; gap: .375rem;
  padding: .5rem .75rem; font-size: .875rem; font-weight: 500;
  color: var(--secondary-foreground); border-bottom: 2px solid transparent; background: none; }
.sp-tabs__btn:hover { color: var(--foreground); }
.sp-tabs__btn.is-on { color: var(--primary); border-bottom-color: var(--primary); }
.sp-tabs__n { font-size: .6875rem; background: var(--muted); border-radius: 9999px;
  padding: 0 .375rem; color: var(--secondary-foreground); }

/* ---- the checklist ---- */
.sp-list { display: flex; flex-direction: column; gap: 1rem; }
.sp-grp { background: var(--background); border: 1px solid var(--border); border-radius: .625rem; }
.sp-grp__head { display: flex; align-items: center; gap: .5rem; padding: .625rem .75rem;
  border-bottom: 1px solid var(--border); }
.sp-grp__caret { display: inline-flex; padding: .125rem; color: var(--muted-foreground);
  transition: transform .12s; background: none; }
.sp-grp__caret.is-shut { transform: rotate(-90deg); }
.sp-grp__name { font-size: .8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--secondary-foreground); }
.sp-grp__count { margin-inline-start: auto; font-size: .75rem; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums; }
.sp-grp__x { padding: 0 .25rem; color: var(--muted-foreground); font-size: 1.125rem;
  line-height: 1; background: none; }
.sp-grp__x:hover { color: var(--destructive); }
.sp-grp__body { padding: .25rem 0 .5rem; }

/* One task. Grid rather than flex so the columns line up down the whole list —
   a ragged right edge is what makes a checklist hard to scan. */
/* ⚠️ EIGHT TRACKS, AND EVERY ROW EMITS EIGHT CHILDREN. Grid does not leave a
   hole for a cell you skipped — the next child slides into it — so a row with no
   subtasks would sit its assignee under the subtask column and its date under
   the assignee. The list stops lining up, which is most of what makes a
   checklist scannable. taskRow() always emits an empty span rather than nothing. */
/* ⚠️ FIXED TRACKS, NOT `auto`. Every row is its own grid container, so an `auto`
   track sizes to THAT row's content — the state pill is wider for "Blocked" than
   "To do", and the subtask cell is empty on most rows. Measured: assignee cells
   landed at x=1149 on some rows and x=1151 on others. Two pixels is invisible
   until a longer name makes it ten, and a checklist that does not line up down
   the page is the thing that makes it hard to scan. */
.sp-row { display: grid; align-items: center; gap: .5rem; padding: .3125rem .75rem;
  grid-template-columns: .875rem 1.25rem 4.5rem minmax(0, 1fr) 2.75rem 7rem 6.5rem 2rem 1.25rem; }
.sp-row:hover { background: var(--muted); }
.sp-row.is-sub { padding-inline-start: 2.5rem; }
.sp-row.is-done .sp-row__title { color: var(--muted-foreground); text-decoration: line-through; }
.sp-row__box { width: 1.125rem; height: 1.125rem; border: 1.5px solid var(--input);
  border-radius: .25rem; display: inline-flex; align-items: center; justify-content: center;
  color: var(--primary-foreground); font-size: .625rem; background: none; }
.sp-row__box.is-on { background: var(--primary); border-color: var(--primary); }
.sp-row__box:disabled { opacity: .5; }
.sp-row__title { min-width: 0; font-size: .875rem; overflow-wrap: anywhere; }
.sp-row__who, .sp-row__due { font-size: .75rem; color: var(--secondary-foreground);
  white-space: nowrap; overflow: hidden; min-width: 0; }
.sp-row__who .sp-mini, .sp-row__due .sp-mini { max-width: 100%; }
.sp-row__x { color: var(--muted-foreground); font-size: 1.125rem; line-height: 1; background: none; }
.sp-row__x:hover { color: var(--destructive); }
.sp-row__subs { display: inline-flex; align-items: center; gap: .25rem; font-size: .6875rem;
  color: var(--muted-foreground); background: none; }
.sp-row__flag { color: var(--primary); }

/* The state pill doubles as the control: click to cycle. */
.sp-state { padding: .0625rem .375rem; border-radius: .25rem; font-size: .6875rem;
  font-weight: 600; white-space: nowrap; text-align: center;
  overflow: hidden; text-overflow: ellipsis; }
.sp-state--todo        { background: var(--muted); color: var(--secondary-foreground); }
.sp-state--in_progress { background: var(--info-soft); color: var(--info); }
.sp-state--blocked     { background: color-mix(in oklch, var(--destructive) 12%, transparent); color: var(--destructive); }
.sp-state--done        { background: var(--success-soft); color: var(--success); }
.sp-state:disabled     { opacity: .7; }

/* Compact in-row controls: they read as text until you touch them, so the list
   stays a list rather than a form. */
.sp-mini { border: 1px solid transparent; border-radius: .25rem; background: none;
  font-size: .75rem; color: var(--secondary-foreground); padding: .0625rem .25rem; max-width: 9rem; }
.sp-mini:hover, .sp-mini:focus { border-color: var(--input); background: var(--background); outline: none; }

.sp-add { display: flex; align-items: center; gap: .5rem; padding: .3125rem .75rem;
  color: var(--muted-foreground); }
.sp-add__in { flex: 1; min-width: 0; border: 0; background: none; font-size: .875rem;
  color: var(--foreground); padding: .1875rem 0; }
.sp-add__in::placeholder { color: var(--muted-foreground); }
.sp-add__in:focus { outline: none; }
.sp-add--section { border: 1px dashed var(--border); border-radius: .625rem; padding: .625rem .75rem; }

/* ---- notes + feed ---- */
.sp-note { padding: .75rem 0; border-bottom: 1px solid var(--border); }
.sp-note:last-of-type { border-bottom: 0; }
.sp-note__head { display: flex; align-items: center; gap: .5rem; }
.sp-note__title { font-weight: 600; font-size: .9375rem; flex: 1; min-width: 0; }
.sp-note__pin { color: var(--muted-foreground); background: none; padding: .125rem; }
.sp-note__pin.is-on { color: var(--warning); }
.sp-note__body { margin-top: .25rem; font-size: .875rem; color: var(--secondary-foreground);
  white-space: pre-wrap; }
.sp-feed { display: flex; flex-direction: column; gap: .75rem; }
.sp-feed__row { display: flex; align-items: flex-start; gap: .625rem; }

@media (max-width: 40rem) {
  /* Drop the date and delete columns on a phone; the row stays readable and
     both are still reachable from the task itself. */
  .sp-row { grid-template-columns: .875rem 1.25rem 4.5rem minmax(0, 1fr) 2rem 7rem; }
  .sp-row__due, .sp-row__xcell, .sp-row__subs-cell { display: none; }
}

/* ---- drag to reorder ---- */
/* The grip is the only drag source. Making the whole row draggable swallows text
   selection and fights its own inputs — you cannot drag-select inside a field
   that is itself a drag handle. */
.sp-grip { width: .875rem; color: var(--muted-foreground); cursor: grab;
  font-size: .8125rem; line-height: 1; opacity: 0; transition: opacity .12s;
  user-select: none; text-align: center; }
.sp-grip--off { cursor: default; }
.sp-row:hover .sp-grip, .sp-grp__head:hover .sp-grip { opacity: .7; }
.sp-grip:active { cursor: grabbing; }
.sp-row.is-dragging, .sp-grp.is-dragging { opacity: .4; }

/* A line where it will land, drawn with a box-shadow so it never changes layout
   mid-drag — a border would shift every row below it and make the target move
   out from under the cursor. */
.sp-row.is-dropbefore, .sp-grp.is-dropbefore { box-shadow: inset 0 2px 0 0 var(--primary); }
.sp-row.is-dropafter,  .sp-grp.is-dropafter  { box-shadow: inset 0 -2px 0 0 var(--primary); }

.sp-row__subs-cell, .sp-row__xcell { display: inline-flex; align-items: center; min-width: 0; }

/* ⚠️ MILESTONES NEED THEIR OWN TRACKS. They reuse .sp-row for its padding, hover
   and typography, but a milestone row has six cells, not the task row's eight —
   so without this it inherited the task track list and the TITLE landed in the
   1.25rem checkbox column, wrapping one character per line. Reported from the
   live page 2026-09-24.

   The rule that keeps being learned the hard way: a shared row class owns a
   track list, so ANY variant with a different number of cells needs its own
   grid-template-columns AND must emit every cell, empty ones included. */
.sp-row--ms { grid-template-columns: 1.25rem minmax(0, 1fr) 3.5rem 7rem 7.5rem 2rem 1.25rem; }
.sp-row--ms .sp-row__title { font-size: .9375rem; }

@media (max-width: 52rem) {
  .sp-row--ms { grid-template-columns: 1.25rem minmax(0, 1fr) 7rem 2rem 1.25rem; }
  .sp-row--ms .sp-grp__count, .sp-row--ms .sp-row__due { display: none; }
}

/* --------------------------------------------------------------------------
   Projects — the team view.
   -------------------------------------------------------------------------- */
.sp-sum { display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }
.sp-sum__cell { background: var(--background); border: 1px solid var(--border);
  border-radius: .625rem; padding: .625rem .75rem; }
.sp-sum__n { font-size: 1.375rem; font-weight: 600; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--mono); }
.sp-sum__n.is-bad  { color: var(--destructive); }
.sp-sum__n.is-warn { color: var(--warning); }

.sp-filters { display: flex; flex-wrap: wrap; gap: .5rem; }
.sp-filters > * { flex: 0 1 auto; }
.sp-filters > input { flex: 1 1 14rem; min-width: 10rem; }
/* Projects: search on its own row, the three selects side by side beneath it.
   Flex-wrap alone stacked them because .kt-select is width:100%. */
.sp-filters--grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sp-filters--grid > input { grid-column: 1 / -1; min-width: 0; }
@media (max-width: 640px) { .sp-filters--grid { grid-template-columns: 1fr; } }
.sp-filters__chips { display: flex; flex-wrap: wrap; gap: .375rem; }
.sp-filters__chips:empty { display: none; }

/* An active filter is always named and always removable. A filter you cannot
   see is a filter you cannot undo. */
.sp-fchip { display: inline-flex; align-items: center; gap: .375rem;
  padding: .1875rem .5rem; border-radius: 9999px; font-size: .75rem;
  background: var(--muted); color: var(--secondary-foreground); }
.sp-fchip:hover { background: var(--accent); }
.sp-fchip__x { font-size: .875rem; line-height: 1; color: var(--muted-foreground); }
.sp-fchip--clear { background: transparent; text-decoration: underline; }

/* Same fixed-track discipline as the checklist: every row is its own grid, so
   `auto` would size to that row's content and the columns would not line up. */
.sp-plist { display: flex; flex-direction: column; }
.sp-plist__head, .sp-plist__row {
  display: grid; align-items: center; gap: .75rem; padding: .625rem 1.25rem;
  grid-template-columns: minmax(0, 2fr) 5.5rem 9rem minmax(0, 1.3fr) 11.5rem 5.5rem;
}
.sp-plist__head { font-size: .6875rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted-foreground);
  border-bottom: 1px solid var(--border); }
/* position: relative so .sp-stretch's ::after overlay is bounded by the row. */
.sp-plist__row { position: relative; border-bottom: 1px solid var(--border); color: inherit; }
.sp-plist__row:last-child { border-bottom: 0; }
.sp-plist__row:hover { background: var(--muted); }
.sp-plist__name { display: flex; flex-wrap: wrap; align-items: baseline; gap: .375rem;
  min-width: 0; font-size: .875rem; }
.sp-plist__name b { overflow-wrap: anywhere; }
.sp-plist__code { width: 100%; font-size: .6875rem; color: var(--muted-foreground); }
.sp-plist__prog { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.sp-plist__prog .sp-progress { flex: 1; min-width: 0; }
.sp-plist__pct { font-size: .75rem; color: var(--secondary-foreground);
  font-variant-numeric: tabular-nums; }
.sp-plist__next { font-size: .8125rem; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.sp-plist__who { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem;
  font-size: .75rem; min-width: 0; }
.sp-plist__lead { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-plist__team { display: inline-flex; align-items: center; gap: .125rem; }
.sp-lead { width: 100%; min-width: 0; font-size: .75rem; padding-block: .1875rem; }
.sp-plist__nums { text-align: end; font-size: .8125rem; white-space: nowrap;
  font-variant-numeric: tabular-nums; color: var(--secondary-foreground); }

.sp-newrow { display: flex; align-items: center; gap: .5rem;
  padding: .625rem 1.25rem; border-top: 1px solid var(--border);
  color: var(--muted-foreground); }

@media (max-width: 60rem) {
  /* Drop the columns a phone cannot read rather than letting the row scroll:
     name, health and progress are what a glance is for. */
  .sp-plist__head, .sp-plist__row {
    grid-template-columns: minmax(0, 2fr) 5.5rem 7rem 5rem;
  }
  .sp-plist__next, .sp-plist__head > :nth-child(4) { display: none; }
  .sp-plist__who { display: none; }
  .sp-plist__head > :nth-child(5) { display: none; }
}

/* --------------------------------------------------------------------------
   My work.
   -------------------------------------------------------------------------- */
.sp-seg { display: inline-flex; border: 1px solid var(--input); border-radius: .375rem;
  overflow: hidden; }
.sp-seg__b { padding: .375rem .75rem; font-size: .8125rem; background: var(--background);
  color: var(--secondary-foreground); }
.sp-seg__b + .sp-seg__b { border-inline-start: 1px solid var(--input); }
.sp-seg__b.is-on { background: var(--primary); color: var(--primary-foreground); }

.sp-check { display: inline-flex; align-items: center; gap: .375rem;
  font-size: .8125rem; color: var(--secondary-foreground); white-space: nowrap; }

/* ⚠️ Its own track list. This row shows a project instead of a drag grip and a
   subtask cell, so it cannot share `.sp-row`'s eight tracks — and fixed tracks
   are what keep the columns aligned down the page. */
.sp-row--mw { grid-template-columns: 1.25rem 4.5rem minmax(0, 1.8fr) minmax(0, 1fr) 2rem 6rem; }
.sp-mw__proj { font-size: .75rem; color: var(--secondary-foreground); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-mw__proj:hover { color: var(--primary); }

@media (max-width: 60rem) {
  .sp-row--mw { grid-template-columns: 1.25rem 4.5rem minmax(0, 1fr) 5.5rem; }
  .sp-row--mw .sp-mw__proj, .sp-row--mw .sp-row__who { display: none; }
}

/* --------------------------------------------------------------------------
   Categories.
   -------------------------------------------------------------------------- */
.sp-cats { display: flex; flex-direction: column; }
.sp-cats__row { display: grid; align-items: center; gap: .75rem;
  padding: .625rem 1.25rem; border-bottom: 1px solid var(--border);
  grid-template-columns: .75rem minmax(0, 1fr) 7rem auto 6.5rem 1.25rem; }
.sp-cats__row:last-of-type { border-bottom: 0; }
.sp-cats__row:hover { background: var(--muted); }
.sp-cats__row.is-off { opacity: .6; }
.sp-cats__dot { width: .75rem; height: .75rem; border-radius: 9999px; }
.sp-cats__name { font-size: .875rem; min-width: 0; overflow-wrap: anywhere; }
.sp-cats__use { font-size: .75rem; color: var(--secondary-foreground); white-space: nowrap; }
.sp-cats__colors { display: inline-flex; gap: .1875rem; }
.sp-cats__swatch { width: .875rem; height: .875rem; border-radius: 9999px;
  border: 2px solid transparent; padding: 0; }
.sp-cats__swatch.is-on { border-color: var(--foreground); }
.sp-cats__act { font-size: .75rem; color: var(--secondary-foreground);
  background: none; white-space: nowrap; }
.sp-cats__act:hover { color: var(--primary); }

@media (max-width: 52rem) {
  .sp-cats__row { grid-template-columns: .75rem minmax(0, 1fr) auto 1.25rem; }
  .sp-cats__use, .sp-cats__colors { display: none; }
}

/* --------------------------------------------------------------------------
   Attachments — uploaded files and pasted links, in one list.
   -------------------------------------------------------------------------- */
.sp-row__clip { display: inline-flex; align-items: center; justify-content: center; }
.sp-clip { display: inline-flex; align-items: center; gap: .125rem; background: none;
  color: var(--muted-foreground); font-size: .6875rem; padding: .125rem .25rem;
  border-radius: .25rem; }
.sp-clip:hover { color: var(--foreground); background: var(--background); }
.sp-clip.is-on { color: var(--primary); }

.sp-files { margin: .125rem .75rem .5rem 3.25rem; padding: .5rem .75rem;
  border: 1px solid var(--border); border-radius: .5rem; background: var(--muted); }
.sp-note .sp-files { margin-inline-start: 0; }
.sp-files__none { font-size: .8125rem; }
.sp-file { display: grid; align-items: center; gap: .5rem; padding: .1875rem 0;
  grid-template-columns: 1rem minmax(0, 1fr) auto 1.25rem; }
.sp-file__ico { color: var(--muted-foreground); font-size: .875rem; }
.sp-file__name { font-size: .8125rem; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--primary); }
.sp-file__name:hover { text-decoration: underline; }
.sp-file__meta { font-size: .6875rem; white-space: nowrap; }

.sp-files__add { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin-top: .5rem; }
.sp-files__btn { display: inline-flex; align-items: center; gap: .375rem; cursor: pointer;
  font-size: .75rem; padding: .25rem .5rem; border: 1px solid var(--input);
  border-radius: .375rem; background: var(--background); color: var(--secondary-foreground); }
.sp-files__btn:hover { border-color: var(--primary); color: var(--primary); }
.sp-files__link { flex: 1 1 16rem; min-width: 0; font-size: .75rem;
  border: 1px solid var(--input); border-radius: .375rem; padding: .25rem .5rem;
  background: var(--background); color: var(--foreground); }
.sp-files__hint { font-size: .6875rem; margin-top: .375rem; }

/* --------------------------------------------------------------------------
   Ideas / What ifs, and My notes. Two capture surfaces added 2026-09-24.

   ⚠️ My notes uses the sp-mynote*/sp-mysub* prefixes, NOT sp-note*. The project
   page's Notes tab already owns sp-note, sp-note__head, sp-note__title and
   sp-note__body; reusing them here would have silently restyled that tab.
   -------------------------------------------------------------------------- */
.sp-ideas { display: flex; flex-direction: column; }
.sp-idea {
  display: flex; align-items: flex-start; gap: .75rem;
  padding: .625rem .25rem; border-bottom: 1px solid var(--border);
}
.sp-idea:last-child { border-bottom: 0; }
.sp-idea__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .125rem; }
.sp-idea__title { font-weight: 500; overflow-wrap: anywhere; }
.sp-idea__title[role="button"] { cursor: text; border-radius: .25rem; }
.sp-idea__title[role="button"]:hover { background: var(--muted); }
.sp-idea__meta { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem;
  font-size: .75rem; }
.sp-idea__link { color: var(--primary); text-decoration: underline; }
.sp-idea__acts { display: flex; align-items: center; gap: .25rem; flex-shrink: 0; }
/* A converted idea is history, not work: readable, clearly not pending. */
.sp-idea--done .sp-idea__title { color: var(--muted-foreground); text-decoration: line-through; }

.sp-mynotes { display: flex; flex-direction: column; gap: .5rem; }
.sp-mynote { border: 1px solid var(--border); border-radius: .5rem; overflow: hidden; }
.sp-mynote__head { display: flex; align-items: center; gap: .5rem;
  padding: .5rem .625rem; background: var(--muted); }
.sp-mynote__caret { display: inline-flex; align-items: center; justify-content: center;
  width: 1.25rem; height: 1.25rem; flex-shrink: 0; color: var(--muted-foreground);
  background: none; border: 0; cursor: pointer; }
.sp-mynote__title { font-weight: 600; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.sp-mynote__title[role="button"] { cursor: text; border-radius: .25rem; }
.sp-mynote__title[role="button"]:hover { background: var(--background); }
.sp-mynote__count { font-size: .75rem; white-space: nowrap; }
.sp-mynote__acts { display: flex; align-items: center; gap: .375rem; flex-shrink: 0; }
.sp-mynote__when { font-size: .6875rem; white-space: nowrap; }
.sp-mynote__detail { padding: .625rem; display: flex; flex-direction: column; gap: .5rem; }
.sp-mynote__body { width: 100%; resize: vertical; font-size: .8125rem; }
.sp-mynote__body-ro { font-size: .8125rem; color: var(--secondary-foreground);
  white-space: pre-wrap; }
.sp-mynote__kids { display: flex; flex-direction: column; }

.sp-mysub { display: flex; align-items: flex-start; gap: .5rem; padding: .3125rem 0; }
.sp-mysub__bullet { color: var(--muted-foreground); font-size: .625rem; margin-top: .375rem;
  flex-shrink: 0; }
.sp-mysub__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .25rem; }
.sp-mysub__title { overflow-wrap: anywhere; }
.sp-mysub__title[role="button"] { cursor: text; border-radius: .25rem; }
.sp-mysub__title[role="button"]:hover { background: var(--muted); }
.sp-newrow--sub { padding-inline-start: 1rem; }

/* --------------------------------------------------------------------------
   Previous 12 Month Sales (sales-by-state.js)
   -------------------------------------------------------------------------- */
.sp-sbs-bar { flex: 1 1 auto; min-width: 3rem; height: .5rem; border-radius: 999px; background: var(--muted); overflow: hidden; }
.sp-sbs-bar > span { display: block; height: 100%; border-radius: inherit; background: var(--primary); }
.sp-sbs-notes { list-style: disc; padding-left: 1.1rem; display: grid; gap: .6rem; }
.sp-sbs-up   { color: var(--primary); font-size: .625rem; margin-left: .25rem; }
.sp-sbs-down { color: var(--destructive); font-size: .625rem; margin-left: .25rem; }
.sp-sbs-wrap { white-space: normal !important; min-width: 12rem; }
.sp-sbs-total td { font-weight: 600; color: var(--mono); background: var(--muted); border-top: 1px solid var(--border); }
.sp-sbs-prose { max-width: 52rem; }
/* A no-wrap table's min-content width otherwise propagates up through the shell's flex
   ancestors and widens the whole page; containment makes the wrapper scroll instead. */
.sp-sbs-scroll { contain: inline-size; }

/* --------------------------------------------------------------------------
   E-commerce Toolbox (2026-10-06): ecommerce.html, ecommerce-best-practices.html,
   product-builder.html, sales-channels.html. Built on the theme tokens so it follows
   light/dark like everything else. Prefix sp-ec-.
   -------------------------------------------------------------------------- */
/* Jump-menu targets land below the fixed header instead of under it. */
.sp-ec-lesson, .sp-ec-tools > .kt-card, .sp-ec-channel { scroll-margin-top: 5.5rem; }
.sp-ec-order { list-style: decimal; }
.sp-ec-intro { color: var(--muted-foreground); font-size: .875rem; max-width: 62ch; margin: 0 0 1.25rem; }
.sp-ec-layout { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 1.5rem; align-items: start; }
.sp-ec-toc { position: sticky; top: 5.5rem; display: flex; flex-direction: column; gap: .125rem; font-size: .8125rem; }
.sp-ec-toc__label { font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-foreground); margin: .75rem 0 .25rem; }
.sp-ec-toc__label:first-child { margin-top: 0; }
.sp-ec-toc__link { color: var(--secondary-foreground); padding: .3rem .5rem; border-radius: .375rem; }
.sp-ec-toc__link:hover { background: var(--accent); color: var(--foreground); }
.sp-ec-toc__cta { background: var(--primary); color: var(--primary-foreground); font-weight: 600; padding: .45rem .6rem; border-radius: .375rem; margin-bottom: .375rem; }
.sp-ec-toc__cta:hover { opacity: .9; color: var(--primary-foreground); }
.sp-ec-sub { color: var(--muted-foreground); font-size: .8125rem; }
.sp-ec-muted { color: var(--muted-foreground); font-size: .8125rem; }
.sp-ec-body { font-size: .875rem; line-height: 1.6; color: var(--secondary-foreground); }
.sp-ec-body h3 { font-size: .9375rem; font-weight: 600; color: var(--foreground); margin: 1rem 0 .375rem; }
.sp-ec-body h3:first-child { margin-top: 0; }
.sp-ec-body p { margin: 0 0 .625rem; }
.sp-ec-body ul, .sp-ec-body ol { margin: 0 0 .625rem; padding-left: 1.25rem; display: flex; flex-direction: column; gap: .3rem; }
.sp-ec-body ul { list-style: disc; }
.sp-ec-body ol { list-style: decimal; }
.sp-ec-body a { color: var(--primary); }
.sp-ec-body a:hover { text-decoration: underline; }
.sp-ec-body b, .sp-ec-body strong { color: var(--foreground); font-weight: 600; }
.sp-ec-body code { font-family: ui-monospace, Menlo, monospace; font-size: .8125rem; background: var(--muted); padding: .05rem .3rem; border-radius: .25rem; }
.sp-ec-why, .sp-ec-ex, .sp-ec-anatomy, .sp-ec-dodont { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; margin: .625rem 0 .75rem; }
.sp-ec-why > div { background: var(--muted); border-radius: .5rem; padding: .625rem .75rem; font-size: .8125rem; }
.sp-ec-why b { display: block; font-size: .6875rem; letter-spacing: .06em; text-transform: uppercase; color: var(--primary); margin-bottom: .2rem; }
.sp-ec-why > div:last-child b { color: var(--success); }
.sp-ec-ex__bad, .sp-ec-ex__good { border-radius: .5rem; padding: .55rem .7rem; font-size: .8125rem; }
.sp-ec-ex__bad { background: color-mix(in oklab, var(--destructive) 10%, transparent); }
.sp-ec-ex__good { background: var(--success-soft); }
.sp-ec-ex__bad b, .sp-ec-ex__good b { display: block; font-size: .6875rem; letter-spacing: .06em; text-transform: uppercase; margin-bottom: .15rem; }
.sp-ec-anatomy > div { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem .875rem; }
.sp-ec-anatomy h4 { font-size: .8125rem; font-weight: 600; color: var(--foreground); margin: 0 0 .375rem; }
.sp-ec-anatomy ol { font-size: .8125rem; margin: 0; }
.sp-ec-do, .sp-ec-dont { border-radius: .5rem; padding: .7rem .85rem .55rem; }
.sp-ec-do { background: var(--success-soft); }
.sp-ec-dont { background: color-mix(in oklab, var(--destructive) 10%, transparent); }
.sp-ec-do h4, .sp-ec-dont h4 { font-size: .6875rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; margin: 0 0 .375rem; }
.sp-ec-do h4 { color: var(--success); }
.sp-ec-dont h4 { color: var(--destructive); }
.sp-ec-do ul, .sp-ec-dont ul { font-size: .8125rem; margin: 0; }
.sp-ec-formula { font-weight: 600; font-size: .8125rem; background: var(--muted); color: var(--primary); border-radius: .375rem; padding: .5rem .75rem; margin: .375rem 0 .625rem; }
.sp-ec-table { width: 100%; font-size: .8125rem; border-collapse: collapse; margin: .375rem 0 .75rem; }
.sp-ec-table th, .sp-ec-table td { text-align: left; padding: .45rem .6rem; border-bottom: 1px solid var(--border); }
.sp-ec-table th { color: var(--muted-foreground); font-weight: 600; }
/* tools */
.sp-ec-tools { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; align-items: start; }
.sp-ec-tools--wide { grid-column: 1 / -1; }
.sp-ec-serp { border: 1px solid var(--border); border-radius: .5rem; padding: .875rem 1rem; font-family: Arial, sans-serif; background: var(--background); }
.sp-ec-serp__crumb { font-size: .75rem; color: var(--muted-foreground); }
.sp-ec-serp__title { font-size: 1.15rem; color: #1a0dab; line-height: 1.3; margin: .2rem 0 .1rem; }
.dark .sp-ec-serp__title { color: #8ab4f8; }
.sp-ec-serp__desc { font-size: .8125rem; color: var(--secondary-foreground); line-height: 1.5; }
.sp-ec-meter { display: flex; justify-content: space-between; gap: .5rem; font-size: .75rem; color: var(--muted-foreground); }
.sp-ec-meter--good { color: var(--success); }
.sp-ec-meter--short { color: var(--warning); }
.sp-ec-meter--long, .sp-ec-meter--missing { color: var(--destructive); }
.sp-ec-warn { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .375rem; }
.sp-ec-warn li { font-size: .8125rem; background: var(--warning-soft); border-radius: .375rem; padding: .45rem .65rem; color: var(--foreground); }
.sp-ec-titleout { font-weight: 600; font-size: .875rem; background: var(--muted); border-radius: .375rem; padding: .6rem .75rem; overflow-wrap: anywhere; }
.sp-ec-titleout mark { background: color-mix(in oklab, var(--primary) 18%, transparent); color: inherit; border-radius: .2rem; }
.sp-ec-code { border: 1px solid var(--border); border-radius: .5rem; overflow: hidden; background: var(--muted); }
.sp-ec-code__bar { display: flex; justify-content: space-between; align-items: center; padding: .35rem .5rem .35rem .75rem; border-bottom: 1px solid var(--border); font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-foreground); }
.sp-ec-code pre { margin: 0; padding: .75rem .875rem; font: .75rem/1.55 ui-monospace, Menlo, monospace; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--foreground); }
.sp-ec-score { display: flex; align-items: baseline; gap: .6rem; }
.sp-ec-score b { font-size: 1.875rem; font-weight: 700; color: var(--foreground); }
.sp-ec-bar { height: .375rem; border-radius: .25rem; background: var(--muted); overflow: hidden; }
.sp-ec-bar > i { display: block; height: 100%; background: var(--success); border-radius: .25rem; }
.sp-ec-checks { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .25rem 1.25rem; font-size: .8125rem; }
.sp-ec-checks li { display: flex; gap: .5rem; align-items: flex-start; padding: .3rem 0; border-bottom: 1px solid var(--border); }
.sp-ec-checks__group { grid-column: 1 / -1; font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-foreground); border: 0 !important; padding-top: .75rem !important; }
/* sales channels */
.sp-ec-pills { display: flex; gap: .375rem; flex-wrap: wrap; margin-bottom: 1rem; }
.sp-ec-pills button { font-size: .75rem; font-weight: 600; padding: .45rem .75rem; border-radius: 999px; border: 1px solid var(--border); background: var(--background); color: var(--secondary-foreground); }
.sp-ec-pills button[aria-pressed="true"] { background: var(--primary); border-color: var(--primary); color: var(--primary-foreground); }
.sp-ec-channels { display: grid; grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); gap: 1rem; }
.sp-ec-channel dl { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: .35rem .75rem; margin: 0; font-size: .8125rem; }
.sp-ec-channel dt { font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-foreground); padding-top: .1rem; }
.sp-ec-channel dd { margin: 0; color: var(--secondary-foreground); }
.sp-ec-tags { display: flex; gap: .3rem; flex-wrap: wrap; }
.sp-ec-tag { font-size: .6875rem; font-weight: 600; padding: .15rem .5rem; border-radius: 999px; background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary); }
.sp-ec-tag--free { background: var(--success-soft); color: var(--success); }
.sp-ec-step { font-size: .6875rem; font-weight: 600; color: var(--muted-foreground); white-space: nowrap; }
.sp-ec-ready { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; font-size: .8125rem; }
.sp-ec-ready li { display: flex; gap: .5rem; }
.sp-ec-ready li::before { content: ''; flex: none; width: .875rem; height: .875rem; margin-top: .2rem; border: 1.5px solid var(--muted-foreground); border-radius: .2rem; }
/* product page builder */
.sp-ec-pb { display: grid; grid-template-columns: 14rem minmax(0, 1fr); gap: 1.5rem; align-items: start; }
.sp-ec-steps { position: sticky; top: 5.5rem; display: flex; flex-direction: column; gap: .2rem; }
.sp-ec-steps button { display: flex; gap: .6rem; align-items: center; text-align: left; padding: .45rem .6rem; border-radius: .375rem; font-size: .8125rem; color: var(--secondary-foreground); }
.sp-ec-steps button:hover { background: var(--accent); }
.sp-ec-steps button[aria-current="step"] { background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary); font-weight: 600; }
.sp-ec-steps__n { flex: none; width: 1.5rem; height: 1.5rem; border-radius: 50%; display: grid; place-items: center; font-size: .6875rem; font-weight: 700; background: var(--muted); color: var(--muted-foreground); }
.sp-ec-steps .is-done .sp-ec-steps__n { background: var(--success-soft); color: var(--success); }
.sp-ec-steps [aria-current="step"] .sp-ec-steps__n { background: var(--primary); color: var(--primary-foreground); }
.sp-ec-feat { display: grid; grid-template-columns: minmax(0, 1fr) 1rem minmax(0, 1.3fr) auto; gap: .5rem; align-items: center; }
.sp-ec-feat__arrow { color: var(--muted-foreground); text-align: center; }
.sp-ec-seg { display: inline-flex; border: 1px solid var(--border); border-radius: .5rem; padding: .15rem; gap: .15rem; }
.sp-ec-seg button { font-size: .75rem; font-weight: 600; padding: .35rem .7rem; border-radius: .375rem; color: var(--muted-foreground); }
.sp-ec-seg button[aria-pressed="true"] { background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary); }
.sp-ec-review { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .35rem 1rem; font-size: .8125rem; margin: 0; }
.sp-ec-review dt { color: var(--muted-foreground); }
.sp-ec-review dd { margin: 0; white-space: pre-wrap; color: var(--foreground); }
.sp-ec-writing { display: grid; place-items: center; gap: .6rem; padding: 2.5rem 1rem; text-align: center; color: var(--muted-foreground); font-size: .875rem; }
.sp-ec-spinner { width: 2.75rem; height: 2.75rem; border-radius: 50%; border: 3px solid var(--muted); border-top-color: var(--primary); animation: sp-ec-spin .9s linear infinite; }
@keyframes sp-ec-spin { to { transform: rotate(360deg); } }
.sp-ec-copy { font-size: .9375rem; line-height: 1.65; color: var(--secondary-foreground); }
.sp-ec-copy__hl { font-size: 1.25rem; font-weight: 700; color: var(--foreground); line-height: 1.3; margin: 0 0 .75rem; }
.sp-ec-copy p { margin: 0 0 .75rem; }
.sp-ec-copy ul { list-style: disc; padding-left: 1.4rem; margin: .25rem 0 .9rem; display: flex; flex-direction: column; gap: .35rem; }
.sp-ec-copy__cta { font-weight: 700; color: var(--foreground); }
.sp-ec-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.sp-ec-list li { display: flex; justify-content: space-between; gap: .75rem; align-items: center; background: var(--muted); border-radius: .375rem; padding: .55rem .75rem; font-size: .875rem; }
.sp-ec-list--warn li { background: var(--warning-soft); font-size: .8125rem; }
.sp-ec-chips { display: flex; gap: .375rem; flex-wrap: wrap; }
.sp-ec-chips button { font-size: .75rem; font-weight: 600; padding: .45rem .7rem; border-radius: 999px; border: 1px solid var(--border); background: var(--background); }
.sp-ec-chips button[aria-pressed="true"] { border-color: var(--primary); color: var(--primary); background: color-mix(in oklab, var(--primary) 10%, transparent); }
@media (max-width: 1024px) {
  .sp-ec-layout, .sp-ec-pb, .sp-ec-tools { grid-template-columns: minmax(0, 1fr); }
  .sp-ec-toc, .sp-ec-steps { position: static; flex-direction: row; flex-wrap: wrap; }
  .sp-ec-toc__label { display: none; }
  .sp-ec-checks { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  .sp-ec-why, .sp-ec-ex, .sp-ec-anatomy, .sp-ec-dodont { grid-template-columns: minmax(0, 1fr); }
  .sp-ec-feat { grid-template-columns: minmax(0, 1fr) auto; }
  .sp-ec-feat__arrow { display: none; }
  .sp-ec-channel dl, .sp-ec-review { grid-template-columns: minmax(0, 1fr); }
}

/* Ideas — uploaded images (2026-10-06). Thumbnails at the top of an opened idea; click opens full size. */
.sp-ideaimg { display: flex; flex-wrap: wrap; gap: .5rem; margin: .5rem 0; }
.sp-ideaimg__item { position: relative; margin: 0; }
.sp-ideaimg__item img { display: block; width: 8rem; height: 8rem; object-fit: cover;
  border: 1px solid var(--border); border-radius: .375rem; background: var(--muted); }
.sp-ideaimg__x { position: absolute; top: .25rem; right: .25rem; width: 1.5rem; height: 1.5rem;
  border-radius: 999px; border: 1px solid var(--border); background: var(--background);
  color: var(--muted-foreground); line-height: 1; cursor: pointer; }
.sp-ideaimg__x:hover, .sp-ideaimg__x.is-armed { color: var(--destructive); border-color: currentColor; }


/* Sidebar — light grey ground (2026-11-14, Jeff) so the left menu reads as its
   own panel against the white page. Scoped by #sidebar (beats the markup's
   .bg-background class). --accent is stepped one shade darker INSIDE the
   sidebar only, because the hover / active-item highlight is bg-accent/60 and
   the default zinc-100 accent would vanish on a zinc-100 ground. Dark theme
   mirrors it: sidebar one step lighter than the zinc-950 page. */
#sidebar {
  --sp-sidebar-bg: var(--color-zinc-100);
  --accent: var(--color-zinc-200);
  background-color: var(--sp-sidebar-bg);
  border-inline-end-color: var(--color-zinc-200);
}
.dark #sidebar {
  --sp-sidebar-bg: var(--color-zinc-900);
  --accent: var(--color-zinc-800);
  border-inline-end-color: var(--color-zinc-800);
}
