diff --git a/app/globals.css b/app/globals.css
index 33a4cc9..e5ac6a0 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1720,17 +1720,34 @@ html.theme-vaporwave .vw-palm--right-small {
that inline value (animations sit above inline styles in the cascade) --
the card stops following the pointer. opacity-50 is exactly the class the
card gets while dragging, so the hiccup stands down for the duration.
- (overflow: hidden keeps the tracking band clipped to the card while it
- sweeps; the hover lift uses the separate `translate` property, so the
- jitter doesn't fight it.) */
+ (The tracking band sweeps inside a .vw-glitch-clip clip box rendered by
+ GroupCard rather than relying on overflow: hidden on the card itself --
+ the card must stay overflow-visible so the progress pie, which pokes
+ outside the bottom-right corner, isn't cropped by the card or its
+ rounded-2xl corner. The hover lift uses the separate `translate`
+ property, so the jitter doesn't fight it.) */
html.theme-vaporwave [data-group-card]:not(.opacity-50) {
- overflow: hidden;
animation: vw-card-glitch 10s linear infinite;
}
html.theme-vaporwave [data-group-card]:not(.opacity-50) > div:first-child h3 {
animation: vw-title-glitch 10s linear infinite;
}
-html.theme-vaporwave [data-group-card]:not(.opacity-50)::after {
+/* The band's clip box: a full-card overlay (in GroupCard) that carries the
+ overflow clipping, so the card itself never needs it. inset-0 +
+ border-radius: inherit matches the card's own padding-box clip exactly,
+ sweep included. Hidden in every other theme. */
+.vw-glitch-clip {
+ display: none;
+}
+html.theme-vaporwave .vw-glitch-clip {
+ display: block;
+ position: absolute;
+ inset: 0;
+ overflow: hidden;
+ border-radius: inherit;
+ pointer-events: none;
+}
+html.theme-vaporwave [data-group-card]:not(.opacity-50) .vw-glitch-clip::after {
content: "";
position: absolute;
left: 0;
@@ -1756,14 +1773,14 @@ html.theme-vaporwave [data-group-card]:not(.opacity-50)::after {
first delay elapses, and under reduced motion, everything just sits still.) */
html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n + 2) [data-group-card]:not(.opacity-50),
html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n + 2) [data-group-card]:not(.opacity-50) > div:first-child h3,
-html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n + 2) [data-group-card]:not(.opacity-50)::after {
+html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n + 2) [data-group-card]:not(.opacity-50) .vw-glitch-clip::after {
animation-duration: 13s;
animation-delay: 3s;
}
/* Phase C: the longest period, offset furthest. */
html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n) [data-group-card]:not(.opacity-50),
html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n) [data-group-card]:not(.opacity-50) > div:first-child h3,
-html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n) [data-group-card]:not(.opacity-50)::after {
+html.theme-vaporwave [data-category-lane] .overflow-y-auto > div:nth-child(3n) [data-group-card]:not(.opacity-50) .vw-glitch-clip::after {
animation-duration: 17s;
animation-delay: 7s;
}
diff --git a/components/board/category-lane.tsx b/components/board/category-lane.tsx
index fd2b2f6..f8fd4c5 100644
--- a/components/board/category-lane.tsx
+++ b/components/board/category-lane.tsx
@@ -162,7 +162,16 @@ export function CategoryLane({ category }: { category: CategoryDTO }) {
// leaves enough clearance that a card's hover lift
// (-translate-y-0.5 in GroupCard) doesn't tuck its top stroke
// under the lane header above it.
- "flex-1 space-y-2 overflow-y-auto rounded-t-lg px-2.5 pt-1.5 pb-2 transition-colors",
+ //
+ // overflow-x-clip: the lane is a fixed-width (w-72) panel whose
+ // cards always span it, so horizontal scrolling is never wanted.
+ // The only things that can spill horizontally are corner badges
+ // like the group card's progress pie, which deliberately poke
+ // past the card's right edge. `clip` (not `hidden`) keeps
+ // overflow-y-auto fully functional while simply discarding any
+ // spill, so a future badge that strays a px too far can never
+ // grow a stray horizontal scrollbar here.
+ "flex-1 space-y-2 overflow-y-auto overflow-x-clip rounded-t-lg px-2.5 pt-1.5 pb-2 transition-colors",
isDropTargetLane && "bg-primary/10"
)}
>
diff --git a/components/board/group-card.tsx b/components/board/group-card.tsx
index 0a97b67..2cc8a8d 100644
--- a/components/board/group-card.tsx
+++ b/components/board/group-card.tsx
@@ -406,7 +406,11 @@ export function GroupCard({ group }: { group: GroupDTO }) {
Archive (all to-dos done)
)}
-
+ {/* pr-2.5: the corner progress pie (TodoProgressPie) hugs
+ the card's right edge right here, so the label keeps a
+ little room of its own -- together the two clear each
+ other without either spilling past the lane's edge. */}
+
{completedCount}/{group.todos.length} done
@@ -421,6 +425,14 @@ export function GroupCard({ group }: { group: GroupDTO }) {
)}
>
)}
+
+ {/* Vaporwave's VHS tracking band sweeps inside this clip box (see
+ .vw-glitch-clip in app/globals.css) instead of the card itself
+ using overflow: hidden -- the card must stay overflow-visible so
+ the progress pie can poke past the bottom-right corner without
+ being clipped by it or the card's rounded corner. display:none
+ (and thus inert) in every other theme. */}
+
{editingTodo && (
diff --git a/components/board/todo-progress-pie.tsx b/components/board/todo-progress-pie.tsx
index 47b99ef..ee43a90 100644
--- a/components/board/todo-progress-pie.tsx
+++ b/components/board/todo-progress-pie.tsx
@@ -40,7 +40,20 @@ export function TodoProgressPie({
// context (see hover:-translate-y-0.5 in GroupCard), so
// without an explicit z-index here it paints on top and crops
// the badge's overlapping edge.
- className="absolute -right-1.5 -bottom-1.5 z-10 size-7 shrink-0 cursor-default rounded-full border-2 shadow-sm outline-none transition-transform hover:scale-110 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
+ // -right-2.5: the badge's horizontal position is a budget, not
+ // a taste call. From the card's right border there are exactly
+ // 10px of lane (CategoryLane's px-2.5) before the lane's own
+ // clip edge -- or its vertical scrollbar, in classic-scrollbar
+ // browsers, which sits at the same 10px mark and paints over
+ // anything past it. So the badge may stick out at most ~7px
+ // (10px offset from the card's *padding* edge minus the 3px
+ // border) to stay clear of both. In exchange the "N/N done"
+ // footer text carries pr-2.5, which hands this badge the room
+ // it needs to clear the text (see the span in GroupCard).
+ // Moving it further out looks like it "fits" until it doesn't:
+ // past the 10px mark the lane clips the badge's edge or grows
+ // a stray horizontal scrollbar.
+ className="absolute -right-2.5 -bottom-1.5 z-10 size-7 shrink-0 cursor-default rounded-full border-2 shadow-sm outline-none transition-transform hover:scale-110 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
style={{
borderColor: accentColor,
background: `conic-gradient(${accentColor} ${pct}%, ${emptyColor} ${pct}% 100%)`,