/* TDB Dev Pulse v2 — release-velocity widget (homepage compact + features full). */
#ts-pulse { min-height: 320px; }
#ts-pulse[aria-busy="true"] {
  background: linear-gradient(110deg, rgba(148,163,184,.05) 8%, rgba(148,163,184,.10) 18%, rgba(148,163,184,.05) 33%);
  background-size: 200% 100%;
  animation: tsp-shimmer 1.4s linear infinite;
  border: 1px solid var(--border, color-mix(in srgb, var(--tdb-line) 16%, transparent));
  border-radius: 14px;
}
@keyframes tsp-shimmer { to { background-position: -200% 0; } }

/* staggered card reveal */
.tsp-pre { opacity: 0; transform: translateY(18px); }
.tsp-vis { opacity: 1; transform: none; transition: opacity .7s cubic-bezier(.22,1,.36,1), transform .7s cubic-bezier(.22,1,.36,1); }

/* shared card block */
.tsp-card-block { background: var(--surface, color-mix(in srgb, var(--tdb-ground) 55%, transparent)); border: 1px solid var(--border, color-mix(in srgb, var(--tdb-line) 16%, transparent));
  border-radius: 14px; padding: 18px 20px; margin: 0 0 18px; }
.tsp-head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
.tsp-title { font-family: var(--mono, monospace); font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-2, var(--tdb-body)); }
.tsp-title-sub { font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: .08em; color: var(--text-3, var(--tdb-muted)); }

/* stats */
.tsp-stats { display: grid; gap: 6px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--surface, color-mix(in srgb, var(--tdb-ground) 55%, transparent)); border: 1px solid var(--border, color-mix(in srgb, var(--tdb-line) 16%, transparent));
  border-radius: 14px; padding: 8px; margin: 0 0 18px; }
.tsp-stat { padding: 16px 12px; text-align: center; border-right: 1px dashed var(--hairline, color-mix(in srgb, var(--tdb-line) 22%, transparent)); }
.tsp-stat:last-child { border-right: 0; }
.tsp-stat-num { font-size: 29px; font-weight: 800; color: var(--accent-ink, var(--tdb-body));
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1.1; min-height: 32px; }
.tsp-stat-label { font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-2, var(--tdb-body)); margin: 8px 0 0; line-height: 1.6; }
.tsp-stat-sub { color: var(--text-3, var(--tdb-muted)); font-size: 9.5px; letter-spacing: .06em; text-transform: none; }

/* chart */
.tsp-chart svg { display: block; width: 100%; height: 150px; }
.tsp-ticks { position: relative; height: 16px; margin-top: 4px; }
.tsp-ticks i { position: absolute; transform: translateX(-50%); font-style: normal;
  font-family: var(--mono, monospace); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-3, var(--tdb-muted)); }

/* category bars */
.tsp-cat { display: grid; grid-template-columns: 170px 1fr 70px; gap: 12px; align-items: center; margin: 10px 0; }
.tsp-cat-name { font-size: 13px; color: var(--text, var(--tdb-text)); font-weight: 600; }
.tsp-cat-track { height: 10px; border-radius: 5px; background: rgba(148,163,184,.10); overflow: hidden; }
.tsp-cat-bar { height: 100%; width: 0; border-radius: 5px; transition: width 1.1s cubic-bezier(.22,1,.36,1); }
.tsp-c-feat { background: linear-gradient(90deg, var(--tdb-accent), var(--tdb-accent)); }
.tsp-c-fix  { background: linear-gradient(90deg, #059669, var(--tdb-success)); }
.tsp-c-imp  { background: linear-gradient(90deg, var(--tdb-support), var(--tdb-support)); }
.tsp-c-sec  { background: linear-gradient(90deg, #d97706, var(--tdb-warning)); }
.tsp-cat-num { font-family: var(--mono, monospace); font-size: 13px; font-weight: 700;
  color: var(--accent-ink, var(--tdb-body)); text-align: right; font-variant-numeric: tabular-nums; }

/* milestones timeline */
.tsp-ms-track { display: flex; gap: 0; overflow-x: auto; scroll-snap-type: x proximity;
  padding: 6px 2px 10px; scrollbar-width: thin; position: relative; }
.tsp-ms { min-width: 240px; max-width: 240px; scroll-snap-align: start; position: relative;
  padding: 0 18px 0 0; }
.tsp-ms-date { font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: .08em;
  color: var(--text-3, var(--tdb-muted)); display: block; margin-bottom: 8px; white-space: nowrap; }
.tsp-ms-dot { display: block; position: relative; height: 12px; margin-bottom: 12px; }
.tsp-ms-dot::before { content: ""; position: absolute; left: 0; top: 50%; right: 0; height: 2px;
  background: color-mix(in srgb, var(--tdb-accent) 25%, transparent); transform: translateY(-50%); }
.tsp-ms:last-child .tsp-ms-dot::before { right: 18px; }
.tsp-ms-dot::after { content: ""; position: absolute; left: 0; top: 50%; width: 12px; height: 12px;
  border-radius: 50%; background: var(--tdb-accent); border: 2px solid color-mix(in srgb, var(--tdb-line) 90%, transparent);
  transform: translateY(-50%); box-shadow: 0 0 0 3px rgba(37,99,235,.25); }
.tsp-ms:last-child .tsp-ms-dot::after { background: var(--tdb-success); box-shadow: 0 0 0 3px rgba(52,211,153,.3), 0 0 14px rgba(52,211,153,.5); }
.tsp-ms-t { font-size: 14.5px; font-weight: 700; color: var(--text, var(--tdb-text)); margin: 0 0 6px; line-height: 1.3; }
.tsp-ms-s { font-size: 12.5px; color: var(--text-2, var(--tdb-body)); line-height: 1.5; margin: 0; }

/* heatmap */
.tsp-legend { display: inline-flex; align-items: center; gap: 4px; }
.tsp-legend i { font-style: normal; font-family: var(--mono, monospace); font-size: 10px; color: var(--text-3, var(--tdb-muted)); padding: 0 4px; }
.tsp-legend b { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.tsp-heat { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 13px);
  gap: 3px; overflow-x: auto; padding-bottom: 4px; }
.tsp-cell { width: 13px; height: 13px; border-radius: 3px; }
@media (prefers-reduced-motion: no-preference) {
  .tsp-cell { opacity: 0; animation: tsp-in .4s ease forwards; }
}
@keyframes tsp-in { to { opacity: 1; } }
.tsp-l0 { background: rgba(148,163,184,.10); }
.tsp-l1 { background: color-mix(in srgb, var(--tdb-accent) 30%, transparent); }
.tsp-l2 { background: color-mix(in srgb, var(--tdb-accent) 55%, transparent); }
.tsp-l3 { background: color-mix(in srgb, var(--tdb-accent) 80%, transparent); }
.tsp-l4 { background: var(--tdb-accent); }

/* releases slider */
.tsp-nav { display: inline-flex; gap: 8px; }
.tsp-btn { width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--border-strong, color-mix(in srgb, var(--tdb-line) 32%, transparent));
  background: rgba(255,255,255,.04); color: var(--text, var(--tdb-text)); cursor: pointer; font-size: 15px;
  transition: border-color .18s ease, background .18s ease, transform .15s ease; }
.tsp-btn:hover { border-color: var(--accent-ink, #9bb8ef); background: color-mix(in srgb, var(--tdb-accent) 8%, transparent); transform: translateY(-1px); }
.tsp-btn:focus-visible { outline: 2px solid var(--accent, var(--tdb-accent)); outline-offset: 2px; }
.tsp-slider { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
  padding: 2px 2px 8px; scrollbar-width: thin; }
.tsp-rel { min-width: 280px; max-width: 280px; scroll-snap-align: start;
  background: var(--surface-2, color-mix(in srgb, var(--tdb-ground) 35%, transparent)); border: 1px solid var(--border, color-mix(in srgb, var(--tdb-line) 16%, transparent));
  border-radius: 10px; padding: 14px 16px; transition: border-color .2s ease, transform .2s ease; }
.tsp-rel:hover { border-color: color-mix(in srgb, var(--tdb-accent) 40%, transparent); transform: translateY(-2px); }
.tsp-rel-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.tsp-ver { font-family: var(--mono, monospace); font-size: 12px; font-weight: 700;
  color: var(--accent-ink, var(--tdb-body)); background: color-mix(in srgb, var(--tdb-accent) 10%, transparent); border: 1px solid color-mix(in srgb, var(--tdb-accent) 30%, transparent);
  border-radius: 999px; padding: 3px 10px; white-space: nowrap; }
.tsp-date { font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: .06em;
  color: var(--text-3, var(--tdb-muted)); white-space: nowrap; }
.tsp-sum { font-size: 13.5px; line-height: 1.55; color: var(--text-2, var(--tdb-body)); margin: 0;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }

/* foot + homepage CTA */
.tsp-foot { font-family: var(--mono, monospace); font-size: 11.5px; color: var(--text-3, var(--tdb-muted));
  text-align: center; margin: 14px 0 0; line-height: 1.7; }
.tsp-more { color: var(--accent-ink, var(--tdb-body)); text-decoration: none; font-size: 12.5px;
  border-bottom: 1px dashed color-mix(in srgb, var(--tdb-accent) 40%, transparent); padding-bottom: 2px; transition: color .18s ease, border-color .18s ease; }
.tsp-more:hover { color: #fff; border-color: rgba(255,255,255,.5); }

/* light mode (both site conventions) */
html[data-theme="light"] .tsp-stat-num, body.light-mode .tsp-stat-num { color: var(--tdb-accent); }
html[data-theme="light"] .tsp-l0, body.light-mode .tsp-l0 { background: color-mix(in srgb, var(--tdb-ground) 8%, transparent); }
html[data-theme="light"] .tsp-btn, body.light-mode .tsp-btn { background: #fff; color: var(--tdb-on-light); }
html[data-theme="light"] .tsp-ver, body.light-mode .tsp-ver { color: var(--tdb-accent); }
html[data-theme="light"] .tsp-sum, body.light-mode .tsp-sum { color: #475569; }
html[data-theme="light"] .tsp-ms-t, body.light-mode .tsp-ms-t { color: var(--tdb-on-light); }
html[data-theme="light"] .tsp-ms-s, body.light-mode .tsp-ms-s { color: #475569; }
html[data-theme="light"] .tsp-cat-name, body.light-mode .tsp-cat-name { color: var(--tdb-on-light); }
html[data-theme="light"] .tsp-cat-num, body.light-mode .tsp-cat-num { color: var(--tdb-accent); }
html[data-theme="light"] .tsp-ms-dot::after, body.light-mode .tsp-ms-dot::after { border-color: #fff; }
html[data-theme="light"] .tsp-more, body.light-mode .tsp-more { color: var(--tdb-accent); }
html[data-theme="light"] .tsp-more:hover, body.light-mode .tsp-more:hover { color: var(--tdb-on-light); border-color: color-mix(in srgb, var(--tdb-line) 50%, transparent); }
html[data-theme="light"] .tsp-rel:hover, body.light-mode .tsp-rel:hover { border-color: color-mix(in srgb, var(--tdb-accent) 50%, transparent); }

@media (max-width: 640px) {
  .tsp-stat { border-right: 0; border-bottom: 1px dashed var(--hairline, color-mix(in srgb, var(--tdb-line) 22%, transparent)); }
  .tsp-stat:last-child { border-bottom: 0; }
  .tsp-rel { min-width: 240px; max-width: 240px; }
  .tsp-ms { min-width: 210px; max-width: 210px; }
  .tsp-cat { grid-template-columns: 110px 1fr 60px; }
  .tsp-cat-name { font-size: 12px; }
  .tsp-chart svg { height: 110px; }
}

/* === v2.1 motion pass === */
.tsp-live { display: flex; justify-content: center; align-items: center; gap: 8px; margin: 0 0 16px; }
.tsp-live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--tdb-success); }
.tsp-live-txt { font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--tdb-success); }
.tsp-chart-wrap { position: relative; overflow: hidden; border-radius: 8px; }
.tsp-chart-dot { position: absolute; width: 9px; height: 9px; border-radius: 50%;
  background: var(--tdb-accent); transform: translate(-50%, -50%); pointer-events: none; }
.tsp-chart-sheen { position: absolute; top: 0; bottom: 0; width: 90px; left: -120px;
  background: linear-gradient(105deg, transparent, rgba(148, 197, 255, .07), transparent);
  pointer-events: none; }
.tsp-cell { transition: transform .15s ease; }
.tsp-cell:hover { transform: scale(1.4); }
@media (prefers-reduced-motion: no-preference) {
  .tsp-live-dot { animation: tsp-pulse 2.2s ease-out infinite; }
  .tsp-chart-dot { animation: tsp-pulse-blue 2.4s ease-out infinite .8s; }
  .tsp-chart-sheen { animation: tsp-sheen 7s ease-in-out infinite 2s; }
  .tsp-latest { animation: tsp-in .4s ease forwards, tsp-pulse-cell 2.6s ease-out infinite 1.4s; }
  .tsp-glow { animation: tsp-glow 1.1s ease-out 1; }
  .tsp-ms-dot::before { background: repeating-linear-gradient(90deg, color-mix(in srgb, var(--tdb-accent) 32%, transparent) 0 7px, color-mix(in srgb, var(--tdb-accent) 8%, transparent) 7px 14px);
    animation: tsp-march 1.6s linear infinite; }
}
@keyframes tsp-pulse { 0% { box-shadow: 0 0 0 0 rgba(52,211,153,.55); } 70% { box-shadow: 0 0 0 9px rgba(52,211,153,0); } 100% { box-shadow: 0 0 0 0 rgba(52,211,153,0); } }
@keyframes tsp-pulse-blue { 0% { box-shadow: 0 0 0 0 rgba(96,165,250,.55); } 70% { box-shadow: 0 0 0 10px rgba(96,165,250,0); } 100% { box-shadow: 0 0 0 0 rgba(96,165,250,0); } }
@keyframes tsp-pulse-cell { 0% { box-shadow: 0 0 0 0 rgba(96,165,250,.6); } 60% { box-shadow: 0 0 0 6px rgba(96,165,250,0); } 100% { box-shadow: 0 0 0 0 rgba(96,165,250,0); } }
@keyframes tsp-sheen { 0% { left: -120px; } 55% { left: 110%; } 100% { left: 110%; } }
@keyframes tsp-glow { 0% { text-shadow: 0 0 0 rgba(96,165,250,0); } 35% { text-shadow: 0 0 22px rgba(96,165,250,.65); } 100% { text-shadow: 0 0 0 rgba(96,165,250,0); } }
@keyframes tsp-march { to { background-position: 14px 0; } }


/* === CONTRAST FIX 2026-08-21 ==============================================
   The release-feed captions were #64748b at 9.5-11.5px on the near-black
   panel: 3.91-4.21:1 against a 4.5 requirement. This is the widget that is
   meant to prove the product is alive, so its dates need to be readable.
   #94a3b8 gives 6.4-7.7:1 on the same panels; light mode keeps its own value.
   ================================================================= */
.tsp-date,
.tsp-stat-sub,
.tsp-title-sub,
.tsp-ms-date,
.tsp-foot,
.tsp-ms i,
.tsp-chart i,
.tsp-legend i {
    color: var(--tdb-body);
}
body.light-mode .tsp-date,
body.light-mode .tsp-stat-sub,
body.light-mode .tsp-title-sub,
body.light-mode .tsp-ms-date,
body.light-mode .tsp-foot,
body.light-mode .tsp-ms i,
body.light-mode .tsp-chart i,
body.light-mode .tsp-legend i {
    color: #475569;
}

/* Both the milestone rail and the release slider are horizontal scrollers whose
   items are wider than the last visible slot, so an item is always cut off at
   the edge. That is how a scroller works - but a hard cut reads as a rendering
   fault rather than as "there is more this way".
   The mask fades BOTH edges: the milestone rail opens scrolled to the newest
   entry, so the clipped side is the left one, and fading only the right would
   have fixed the half nobody was looking at. */
.tsp-ms-track,
.tsp-slider{
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 40px,#000 calc(100% - 44px),transparent 100%);
          mask-image:linear-gradient(to right,transparent 0,#000 40px,#000 calc(100% - 44px),transparent 100%);
}
@media (prefers-reduced-motion:reduce){
  .tsp-ms-track,.tsp-slider{scroll-behavior:auto}
}
