diff --git a/src/server/team.ts b/src/server/team.ts index b148492..846fcd8 100644 --- a/src/server/team.ts +++ b/src/server/team.ts @@ -202,8 +202,8 @@ export function renderTeamHtml(cwd: string): string { /* Floating status chip that straddles the top-right edge (Paperclip style). */ .node-badge { position: absolute; top: -9px; right: 12px; z-index: 3; font: 700 9.5px/1 var(--font-mono); text-transform: capitalize; letter-spacing: .02em; padding: 4px 9px; border-radius: 999px; } .node-badge:empty { display: none; } - .node-badge.active { color: #0b3d1e; background: #7ee2a8; box-shadow: 0 2px 12px rgba(34,197,94,.4); } - .node-badge.review { color: #3d2e07; background: #f0cf7a; box-shadow: 0 2px 12px rgba(210,153,34,.35); } + .node-badge.active { color: #15803d; background: #ffffff; box-shadow: 0 2px 12px rgba(34,197,94,.45); } + .node-badge.review { color: #7a5a12; background: #ffffff; box-shadow: 0 2px 12px rgba(210,153,34,.4); } .node-info-btn { flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--border); background: var(--raised); color: var(--muted); font: 700 11px/1 var(--font-mono); cursor: pointer; align-self: flex-start; transition: color 150ms, border-color 150ms; } .node-info-btn:hover { color: var(--text); border-color: var(--accent); } @@ -219,10 +219,26 @@ export function renderTeamHtml(cwd: string): string { .st-link.review { color: var(--status-review); } .st-link:hover { text-decoration: underline; } - .node.active { border-color: rgba(34,197,94,.6); box-shadow: 0 0 0 1px rgba(34,197,94,.5), 0 0 24px rgba(34,197,94,.15); } + /* Active card: a green highlight sweeps along the border L→R while the task runs. */ + .node::before { + content: ''; position: absolute; inset: 0; border-radius: 14px; padding: 1.6px; + background: linear-gradient(90deg, rgba(34,197,94,0) 32%, #22c55e 50%, rgba(34,197,94,0) 68%); + background-size: 260% 100%; background-position: -50% 0; + -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); + -webkit-mask-composite: xor; + mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); + mask-composite: exclude; + opacity: 0; transition: opacity 340ms ease; pointer-events: none; z-index: 2; + } + .node.active { border-color: rgba(34,197,94,.4); box-shadow: 0 0 20px rgba(34,197,94,.10); } + .node.active::before { opacity: 1; animation: borderSweep 2.6s linear infinite; } + @keyframes borderSweep { 0% { background-position: -50% 0; } 100% { background-position: 150% 0; } } .node.reviewing { border-color: rgba(210,153,34,.55); box-shadow: 0 0 0 1px rgba(210,153,34,.45), 0 0 20px rgba(210,153,34,.12); } - .node.arrive { animation: arrivePop 560ms ease; } - @keyframes arrivePop { 0% { box-shadow: 0 0 0 0 rgba(34,197,94,.6); } 100% { box-shadow: 0 0 0 15px rgba(34,197,94,0); } } + /* Gentle, non-jerky glow fades for pulse-arrival and task completion. */ + .node.arrive { animation: arriveGlow 700ms ease; } + @keyframes arriveGlow { 0% { box-shadow: 0 0 0 1px rgba(34,197,94,.55), 0 0 18px rgba(34,197,94,.28); } 100% { box-shadow: 0 0 0 1px rgba(34,197,94,0), 0 0 0 rgba(34,197,94,0); } } + .node.done-flash { animation: doneFade 900ms ease; } + @keyframes doneFade { 0% { box-shadow: 0 0 0 1px rgba(34,197,94,.5); } 45% { box-shadow: 0 0 0 1px rgba(34,197,94,.4), 0 0 22px rgba(34,197,94,.22); } 100% { box-shadow: 0 0 0 1px rgba(34,197,94,0); } } .edge-base { fill: none; stroke: var(--border); stroke-width: 1.5; animation: edgeBreath 4.5s ease-in-out infinite; } @keyframes edgeBreath { 0%,100% { opacity: .35; } 50% { opacity: .62; } } @@ -257,7 +273,7 @@ export function renderTeamHtml(cwd: string): string {