Systematic-debugging: Konrad reported the project/team popovers showed
no options ('can not see any options') and wheel scroll fell through to
the page instead of scrolling the dropdown.
Root cause chain:
1. The 0bbf2ca/ffb3ef6 CSS on .filter-popover had `overflow: hidden`
(to hide anything past max-height) and the body had
`overflow-y: auto; flex: 1 1 auto`.
2. Choices.js renders its option list with the default
`.choices__list--dropdown { position: absolute; }`.
3. Absolutely-positioned elements do NOT contribute to an ancestor's
scrollHeight, so the body's overflow-y: auto never created a scroll
context — wheel events bubbled to the page.
4. The dropdown extended past the popover's bottom edge and got clipped
by the popover's overflow: hidden, so no options were visible.
Single-point fix:
- Remove `overflow: hidden` from .filter-popover (it was only there to
enforce the sticky footer, which the flex layout already does).
- Scoped CSS override on .choices__list--dropdown inside .filter-popover
to force `position: static` — dropdown now flows inline, the body
grows to contain it, and the sticky footer pushes below naturally.
The dropdown gets its own `max-height: 260px; overflow-y: auto` for
long option lists, which gives a clean internal scroll.
Specificity gotcha: Choices.js's rule is
`.choices__list--dropdown, .choices__list[aria-expanded]` — the second
branch has class+attribute specificity (0,0,2,0) that TIES with a naive
two-class override, and since Choices.js's stylesheet loads after ours,
source order gave them the win. The fix is to mirror the selector list,
lifting our specificity to (0,0,2,1) on the aria-expanded branch, which
wins cleanly without `!important`. Inline comment in custom.css explains
this for future reference.
Scoping: the override is gated to `.filter-popover` descendants, so
Choices.js widgets elsewhere in the app (worker / team / project picker
on edit pages, payroll modals, etc.) keep their default absolute-
positioned dropdown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Konrad's Checkpoint-1 feedback: the popover 'kind of disappears against
the report page'. Previous 1px border + 0.28 opacity shadow read as
weak against the amber-tinted report cards.
Now: 2px accent-orange border + three-layer shadow (soft accent halo,
deep drop, near edge) so the popover reads as clearly detached. Light
theme gets its own shadow palette because white-on-white needs less
absolute darkness but more tinting to be visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Checkpoint 1 UX feedback (Konrad, 2026-04-23) surfaced three friction points
that all traced back to the same over-engineered "multi-stage commit" model:
1. When Choices.js opened its dropdown, it covered the popover's OK button.
User had to click in a thin strip "outside the multi-select but inside
the dropdown pane" to close Choices.js before OK became reachable.
2. Changing only a project/team didn't light up the global Apply button
(dirty-state diff bug on multi-selects), and even when it did, clicking
Apply didn't actually update the report tables. Also the Apply button
sat at the far right of the pill strip — easy to miss on desktop.
3. Single-month reports required changing BOTH From and To pickers; for a
low-frequency admin tool, that's a tax on the most common flow.
Instead of patching three bugs, collapsed the entire pending/dirty/Apply
model. Each popover's OK now:
- Rebuilds the URL from its OWN inputs only (keeping other filters intact)
- Navigates → full SSR page reload → report re-renders
The user reads the result of their change immediately; there's no "did I
remember to click Apply?" step.
Side-effect wins:
- 'dirty state', 'pending state', 'updateAllPillsDirty', 'revert...',
cross-filter auto-removal, and the toast system all become unnecessary.
Net -187 lines across template + CSS.
- The bug from (2) self-disappears because there's no dirty-diff step.
- Sticky popover footer (position: sticky; bottom: 0; z-index: 2) pins
OK to the popover edge even when Choices.js expands — solves (1).
- The To month picker is labelled "Until (optional)" with "Leave blank
for a single month" hint. Blank on submit → to_month = from_month.
Single-month URLs round-trip with a blank To input (so the form and
the data agree).
Cross-filter preserved: on popover open, the OTHER pill's URL selection
still disables invalid dropdown options. Just no runtime auto-remove —
unnecessary because the next OK submits and the server takes over.
Tested in the browser via preview MCP:
- All three pills open popovers on click
- Range URL shows both month pickers filled
- Single-month URL shows To blank
- OK with blank To → navigates to from_month=X&to_month=X
- Sticky footer keeps OK in viewport when Choices.js is open
- 45/45 tests still pass (no backend contract change)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final chapter of the executive redesign. Renders the
team_project_activity context as a pivot: rows=teams, columns=projects,
cell=COUNT(DISTINCT work-log dates). Zero cells show em-dashes in muted
grey (not '0') so non-zero cells stand out. Row totals, column totals,
and grand total on the bottom row.
Adds a tiny dictlookup template filter (format_tags.py) — Django
templates can't index a dict by a dynamic variable key, and the pivot
cell lookup is cells_by_project_id[col.id]. Defensive None + TypeError
guards so a malformed context can't 500 the page.
.table-total-row CSS: 2px top border + inset background for the footer
row so totals visually separate from the data rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the old narrow four-card All-Time/YTD row (dropped in Task 9)
with two wider cards under a numbered 'Chapter I - Lifetime Context'
heading. Projects card gains Start, Working Days, Total Cost, and
Avg R / Working Day columns per the design. Teams card keeps name +
total.
Adds .chapter-heading and .chapter-num CSS for the orange numbered
markers (I, II, III, IV) and .report-numeric class that applies
tabular-nums across the number columns of every report table.
Renames the existing 'Selected Period' heading to Chapter II.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chapter 0 of the executive redesign: four large cards at the top
showing Paid This Period, Outstanding Now (live, stamped with the
generation time), FoxFitt Avg/Day, and FoxFitt Avg/Month.
Drops the old four-small-cards All-Time/YTD row (YTD specifically
documented as redundant per design doc section 3). All-Time detail
moves into Chapter I in the next task.
New .stat-card--hero variant uses Poppins 1.85rem for the number,
uppercase tracked labels, subtle tertiary sub-lines. tabular-nums
keeps the R-amounts pixel-aligned across cards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The b0d3829 overrides lost the cascade battle: the Choices.js CDN CSS
loads AFTER custom.css (inside the modal partial near </body>), so the
CDN's same-specificity rules won by load order. Dropdown still showed
white background + light-grey text in dark mode.
Fix: chain the root `.choices` class to every override (specificity
0,2,0 → 0,3,0) and add !important to color + background + border
properties that Choices.js hardcodes most aggressively. Now the
theme tokens always win regardless of load order.
Visual effect: dropdown option hover state now matches the selected
"Month(s)" button aesthetic (--bg-card-hover subtle lift with
--text-primary text) per Konrad's feedback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review of Task 7 showed Choices.js shipping with white-bg +
light-grey-text defaults, which were unreadable in dark mode and
clashed with the app's premium aesthetic. The design doc section 10
scoped these overrides into the CSS work but they hadn't been written.
Adds ~70 lines to custom.css re-themeing every .choices__ selector to
use existing design tokens (--bg-card, --bg-inset, --text-primary,
--accent, --border-default, etc.). No hardcoded colours; both :root
and :root.light themes work automatically.
Key visual changes:
- Dropdown popup: dark-card background with shadow
- Options: primary text colour, accent hover highlight
- Selected chips: orange accent pill with white text
- Focus ring: 0.15rem accent glow on the input
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three pills under the header: date range, project(s), team(s). Shows
comma-joined names when multi-valued (project_name in context is
already a comma-joined string from Task 6). × buttons on the project
and team pills remove just that filter via a rebuilt querystring;
the calendar pill has no × (date range is required).
Helper context keys query_string_without_project / _without_team do
the rebuild in the view via QueryDict.setlist so multi-value keys
are properly stripped (pop() only removes the first occurrence).
Pill CSS uses existing design tokens (--bg-inset, --accent,
--text-primary, --border-default, --text-tertiary, --bg-card-hover)
so dark and light themes work without overrides.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Subtle background tint on hover to cue that the row is clickable.
Applied via .work-log-row class which Tasks 6-8 added to admin-only
rows in work_history.html, teams/detail.html, and projects/detail.html.
Supervisors never get the class, so hover never applies for them.
Deployment_timestamp cache-bust in base.html will beat Cloudflare's
edge cache (per CLAUDE.md Static Assets section).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Override Bootstrap's --bs-table-color to use theme text color so table
numbers (days, amounts, totals) are readable on dark backgrounds. Fix
Loan badge by removing text-dark class and using CSS to force black text
on bg-warning. Add dark mode overrides for disabled form controls, select
option dropdowns, btn-close filter, btn-secondary colors, and Bootstrap
text utility classes (.text-dark, .text-primary, .text-muted, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move decorative gradient glows from ::before/::after pseudo-elements on
.app-main to a separate .app-glow div. The pseudo-elements were creating
a stacking context that trapped Bootstrap modals (z-index 1055) inside
.app-main, while the backdrop (z-index 1050) was appended to <body> —
causing the backdrop to render on top of the modal content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the green accent with a warm orange/amber palette and switch to a
dark-first design. Add a fixed sidebar for desktop navigation and a bottom
tab bar for mobile, replacing the top navbar. Cards now use glass-morphism
with left accent bars, buttons use orange gradients, and decorative glow
effects add depth. All 8 page templates updated, both light and dark modes
tested across desktop and mobile viewports.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>