so
it actually applies. */
main { padding: 24px; max-width: 1400px; margin: 0 auto; }
h2 {
font-size: 18px; margin: 0 0 12px 0; padding-left: 10px;
border-left: 4px solid var(--accent); color: var(--text);
}
.board { display: grid; grid-template-columns: repeat(4, minmax(240px, 1fr)); gap: 16px; }
.column {
background: var(--surface); border: 1px solid var(--border);
border-radius: 6px; padding: 12px; min-height: 400px;
min-width: 0; /* let grid enforce equal columns even if a card has long text */
overflow: hidden; /* clip overflow at column boundary */
}
.column h3 {
font-size: 13px; text-transform: uppercase; letter-spacing: 1px;
margin: 0 0 12px 0; color: var(--text-3);
display: flex; justify-content: space-between; align-items: center;
}
.column h3 .count {
background: var(--surface-2); color: var(--text-2);
padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600;
}
.card {
position: relative;
background: var(--surface-2); border-radius: 4px;
padding: 12px; margin-bottom: 10px; cursor: pointer;
border: 1px solid var(--border); border-left-width: 4px;
transition: transform .08s, box-shadow .08s;
user-select: none;
-webkit-user-select: none;
/* Long unbroken strings (URLs, hashes, file paths) should wrap rather
than spill out of the card boundary. */
overflow-wrap: anywhere; word-break: break-word;
}
.card.P1 {
background: linear-gradient(180deg, rgba(239, 68, 68, 0.34), rgba(220, 38, 38, 0.16));
border-color: rgba(255, 215, 0, 0.96);
/* Make P1 unmistakable: gold ring outside, red spine inside */
border-left: 8px solid #dc2626;
box-shadow:
0 0 0 2px rgba(255, 215, 0, 0.92),
0 0 0 6px rgba(255, 215, 0, 0.18),
0 12px 28px rgba(220, 38, 38, 0.32);
}
.card.P1 h4 {
/* Default title color matches P3 (and all other priorities). The card
background + red 8px spine are what make P1 stand out, not the title. */
}
.card.P2 { background: var(--p2-bg); border-color: var(--p2-border); }
.card.P3 { background: var(--p3-bg); border-color: var(--p3-border); }
.card.P4 { background: var(--p4-bg); border-color: var(--p4-border); }
.card.P5 { background: var(--p5-bg); border-color: var(--p5-border); }
/* Done cards turn green regardless of original priority.
Note: no opacity here -- the low-alpha --done-bg + h4 line-through already
signal "done", and keeping opacity at 1.0 ensures the dragging effect
(opacity: 0.4) produces the same visible drop as for non-done cards. */
.card.done { background: var(--done-bg); border-color: var(--done-border); }
.card.done h4 { text-decoration: line-through; text-decoration-color: var(--text-3); }
.card:hover { transform: translateY(-1px); box-shadow: 0 2px 12px rgba(0,0,0,0.4); }
/* Drag-and-drop visual states.
opacity alone produces inconsistent fade across priorities -- cards with
rgba backgrounds, strong borders, or box-shadows (P1) look different
from flat ones. saturate(0) collapses the color variance so every card
reads as "deeply faded" the same way. */
.card.dragging {
opacity: 0.5;
filter: saturate(0.15) brightness(0.85);
transform: rotate(-1deg) scale(0.98);
border-color: var(--text-4) !important;
box-shadow: none !important;
}
/* Card-arrival animation: a brand-new card fades in with a brief sky-blue
glow ring that mirrors the In Progress status color, then settles.
Triggered by the .card-just-created class (added on the server when a
card's created_at is within the last ~30 seconds). */
@keyframes cardArrival {
0% { opacity: 0; transform: scale(0.96); box-shadow: 0 0 0 4px rgba(14, 165, 233, 0.55); }
60% { opacity: 1; transform: scale(1.005); box-shadow: 0 0 0 6px rgba(14, 165, 233, 0.18); }
100% { opacity: 1; transform: scale(1); box-shadow: 0 0 0 0 rgba(14, 165, 233, 0); }
}
.card-just-created { animation: cardArrival 600ms ease-out; }
/* Button press feedback: scale down slightly on click. 80ms feels instant
but registers the press. Applies to native