diff --git a/TODO.md b/TODO.md
index 1afd4ab..e09dd2a 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,9 +1,11 @@
# TODO
-> Last updated: 2026-06-21
-> Context: Migrate all -based icons to inline SVG via PHP helper so CSS color cascade works
+> Last updated: 2026-06-22
+> Context: Add mobile-responsive accordion + chip bar to repertoire filter index (column headings restored)
## Pending
+- [ ] #rep-student-touch Replace hover student popover with tap-to-open drawer for mobile `(repertoire.php, repertoire.css)`
+- [ ] #rep-polish Polish: scroll-position memory on HTMX swap, animation tuning `(repertoire.css)`
- [ ] #icon-color-verify Verify icon colors render correctly across all pages (header, admin tables, forms, dialogs, cleanup modal)
## Completed
diff --git a/app/public/assets/css/repertoire.css b/app/public/assets/css/repertoire.css
index a760c8f..f412d24 100644
--- a/app/public/assets/css/repertoire.css
+++ b/app/public/assets/css/repertoire.css
@@ -16,7 +16,7 @@
display: grid;
grid-template-columns:
minmax(3rem, 0.45fr)
- minmax(12rem, 1fr)
+ minmax(16rem, 1.2fr)
minmax(9rem, 1fr)
minmax(7rem, 1fr)
minmax(8rem, 1fr)
@@ -29,45 +29,92 @@
height: 100%;
}
-@media (max-width: 1024px) {
- .repertoire-index {
- grid-template-columns: 1fr 1fr 1fr;
- padding: 0;
- grid-template-rows: auto auto 1fr;
- min-height: auto;
+/* ---- Active filter chip bar ---- */
+.rep-chip-bar {
+ display: none;
+}
+
+@media (max-width: 1025px) {
+ .rep-chip-bar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--space-2xs);
+ padding: var(--space-2xs) var(--space-s);
+ border-bottom: 1px solid var(--border-secondary);
+ margin: 0;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ background: var(--bg-primary);
}
}
-@media (max-width: 600px) {
- .repertoire-index {
- grid-template-columns: 1fr;
- padding: 0;
- grid-template-rows: none;
- min-height: auto;
- }
+.rep-chip-bar__label {
+ font-family: var(--font-display);
+ font-size: var(--step--2);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--text-secondary);
+ margin-right: var(--space-3xs);
+ white-space: nowrap;
+}
- /* On single column, each section is a self-contained block again */
- .repertoire-col {
- display: flex;
- flex-direction: column;
- overflow: hidden;
- min-height: 12rem;
- border-bottom: 1px solid var(--border-primary);
- }
+.rep-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-3xs);
+ padding: var(--space-3xs) var(--space-2xs);
+ background: var(--accent-muted);
+ border: 1px solid var(--accent-primary);
+ border-radius: 99px;
+ font-family: var(--font-body);
+ font-size: var(--step--2);
+ color: var(--accent-primary);
+ cursor: pointer;
+ white-space: nowrap;
+ transition: background 0.15s, color 0.15s;
+}
- .repertoire-col:last-child {
- border-bottom: none;
- }
+.rep-chip:hover {
+ background: var(--accent-primary);
+ color: var(--accent-foreground);
+}
- .repertoire-col > h2 {
- grid-row: unset;
- align-self: unset;
- }
+.rep-chip__dim {
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ font-size: 0.75em;
+ color: var(--accent-secondary);
+}
- .repertoire-col > ul {
- grid-row: unset;
- flex: 1;
- }
+.rep-chip:hover .rep-chip__dim {
+ color: inherit;
+ opacity: 0.8;
+}
+
+.rep-chip__remove {
+ font-size: 1.1em;
+ line-height: 1;
+ font-weight: 600;
+}
+
+/* ---- Accordion: visible only on mobile (≤ 640px) ---- */
+
+/* Desktop: heading-text visible, toggle hidden */
+.rep-accordion__heading-text {
+ /* visible — inherits the h2 styling above */
+}
+
+.rep-accordion__toggle,
+.rep-accordion__chevron,
+.rep-accordion__badge {
+ display: none;
+}
+
+.rep-accordion__panel {
+ display: contents;
}
/* Each section becomes transparent to the grid so h2+ul sit in the 2-row layout */
@@ -79,33 +126,215 @@
grid-row: 1;
font-family: var(--font-display);
font-size: var(--step--1);
- letter-spacing: 0.12em;
+ letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-primary);
font-weight: 398;
- line-height: 23px;
+ line-height: 1.3;
margin: 0;
padding: var(--space-xs) 0 var(--space-3xs) 0;
border-bottom: 1px solid var(--text-primary);
align-self: end;
- hyphens: manual;
+ hyphens: auto;
word-break: normal;
- overflow-wrap: normal;
+ overflow-wrap: break-word;
}
.repertoire-col > ul {
grid-row: 2;
overflow-y: auto;
overflow-x: hidden;
- padding: var(--space-2xs) 0 var(--space-l) 0;
+ padding-top: var(--space-2xs);
+ padding-bottom: var(--space-l);
+ padding-left: 0;
+ padding-right: 0;
}
/* Strip list chrome inside repertoire columns */
.repertoire-col ul {
list-style: none;
margin: 0;
+ padding: 0;
}
+/* ---- Responsive (≤ 1025px): accordion mode, chip bar, students as results ---- */
+@media (max-width: 1025px) {
+ .repertoire-index {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ height: 100%;
+ overflow-y: hidden;
+ gap: 0;
+ margin: 0;
+ }
+
+ /* Each column becomes a block-level accordion section */
+ .repertoire-col {
+ display: block;
+ border-bottom: 1px solid var(--text-primary);
+ }
+
+ .repertoire-col[data-col="students"] {
+ border-bottom: none;
+ }
+
+ /* Flex order: years/ap/or/fi first, then keywords, students last */
+ .repertoire-col[data-col="years"] { order: 0; }
+ .repertoire-col[data-col="ap"] { order: 1; }
+ .repertoire-col[data-col="or"] { order: 2; }
+ .repertoire-col[data-col="fi"] { order: 3; }
+ .repertoire-col[data-col="kw"] { order: 4; }
+ .repertoire-col[data-col="students"] { order: 5; }
+
+ /* Students column: not an accordion — open results list below filters */
+ .repertoire-col[data-col="students"] {
+ border-bottom: none;
+ border-top: none;
+ margin-top: 0;
+ padding-top: 0;
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .repertoire-col[data-col="students"] .rep-accordion__toggle {
+ display: none;
+ }
+
+ .repertoire-col[data-col="students"] .rep-accordion__heading-text {
+ display: none;
+ }
+
+ .repertoire-col[data-col="students"] .rep-accordion__panel {
+ display: block;
+ max-height: none;
+ overflow-y: auto;
+ flex: 1;
+ }
+
+ /* Hide the plain h2 — the toggle button replaces it */
+ .repertoire-col > h2 {
+ display: block;
+ grid-row: unset;
+ align-self: unset;
+ border-bottom: none;
+ padding: 0;
+ margin: 0;
+ font-size: inherit;
+ line-height: inherit;
+ }
+
+ /* Hide heading text on mobile (toggle button replaces it) */
+ .rep-accordion__heading-text {
+ display: none;
+ }
+
+ /* Accordion toggle: full-width touch target */
+ .rep-accordion__toggle {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ min-height: 48px;
+ padding: var(--space-xs) var(--space-s);
+ background: none;
+ border: none;
+ font-family: var(--font-display);
+ font-size: var(--step--2);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--text-primary);
+ font-weight: 398;
+ text-align: left;
+ cursor: pointer;
+ }
+
+ .rep-accordion__toggle:active {
+ background: var(--bg-secondary);
+ }
+
+ /* Badge: active filter count */
+ .rep-accordion__badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 1.4em;
+ height: 1.4em;
+ padding: 0 0.35em;
+ margin-left: var(--space-2xs);
+ background: var(--accent-primary);
+ color: var(--accent-foreground);
+ border-radius: 99px;
+ font-family: var(--font-body);
+ font-size: var(--step--2);
+ font-weight: 500;
+ letter-spacing: 0;
+ text-transform: none;
+ }
+
+ /* Chevron: CSS triangle */
+ .rep-accordion__chevron {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ flex-shrink: 0;
+ margin-left: var(--space-2xs);
+ border-right: 2px solid var(--text-secondary);
+ border-bottom: 2px solid var(--text-secondary);
+ transform: rotate(45deg);
+ transition: transform 0.2s;
+ }
+
+ .rep-accordion__toggle[aria-expanded="true"] .rep-accordion__chevron {
+ transform: rotate(-135deg);
+ }
+
+ /* Panel: collapsed by default */
+ .rep-accordion__panel {
+ display: block;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.3s ease;
+ }
+
+ .rep-accordion__panel.is-open {
+ max-height: 60vh;
+ overflow-y: auto;
+ }
+
+ /* Reposition the