refactor dedupe CSS rules, fix comment bug, apply STE to comments

Reduce repetition, fix a real defect, and rewrite comments in Simplified Technical English. No intended visual change, with one exception noted below. - Consolidate the three near-identical modal blocks (confirm, feedback, profile) into shared base, [open], and backdrop rules; only width and padding now differ per dialog. - Replace six per-component <summary> marker resets with two global rules. Every <summary> in the app is a custom control. - Merge the primary-nav and bottom-nav active-state rules, which had identical declarations, into one selector list. - Fix an unterminated comment that closed with the OCaml '*)' instead of '*/'. The comment had swallowed the entire .exercise-name-label rule, so that style never applied. The exercise name in the logging grid now renders bold at the lead size, as intended. This is the one visual change. - Rewrite all comments to ASD-STE100: short active sentences, no prose semicolons, no contractions or Latin abbreviations, specific nouns over ambiguous pronouns, and 'about' in place of the math symbols. - Drop a stale comment claiming the golden scale, and one that restated the table rationale already stated directly above it.

Commit
019f685ea581961aa1b47559ace0e48606d4a535
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
lib/web/assets/hito.css
index 64148d2b..89657ea5 100644..100644
@@ -3,10 +3,10 @@
3 3 :root {
4 4 color-scheme: light;
5 5 /* Uncompromising legibility: pure black text on a very slightly warm white.
6 Removed: The palette stays warm throughout — oxblood primary and a warm bronze
7 Removed: secondary carry the blood-and-effort register — with evergreen reserved for
8 Removed: success. Contrast is the constraint the warmth must respect, not the other
9 Removed: way round. */
6 Added: The palette stays warm throughout. An oxblood primary and a warm bronze
7 Added: secondary carry the blood-and-effort register. Evergreen is reserved for
8 Added: success. Contrast constrains the warmth. The warmth never constrains
9 Added: contrast. */
10 10 --paper: #fdfcfa;
11 11 --paper-raised: #ffffff;
12 12 --on-primary: #ffffff;
@@ -30,12 +30,12 @@
30 30 --font-size-small: 0.75rem;
31 31 --font-size-base: 1rem;
32 32 --font-size-lead: 1.155rem;
33 Removed: /* Heading scale anchored on the perfect fourth. Body is 1rem; each heading
34 Removed: tier steps by the musical fourth (ratio 4:3 ≈ 1.333), so h3 sits one fourth
35 Removed: above body, h2 two fourths (≈1.777), and h1 three fourths (≈2.369). Small
36 Removed: is one fourth below body (≈0.75), and lead is the geometric half-step
37 Removed: (√1.333 ≈ 1.155) between body and h3. A ratio-based hierarchy a reader
38 Removed: perceives, in place of ad-hoc sizes. */
33 Added: /* Heading scale anchored on the perfect fourth. Body is 1rem. Each heading
34 Added: tier steps by the musical fourth, a ratio of 4:3 or about 1.333. h3 sits
35 Added: one fourth above body, h2 two fourths at about 1.777, and h1 three fourths
36 Added: at about 2.369. Small is one fourth below body at about 0.75. Lead is the
37 Added: geometric half-step at about 1.155, between body and h3. A reader
38 Added: perceives this ratio-based hierarchy. It replaces ad-hoc sizes. */
39 39 --font-size-h3: 1.333rem;
40 40 --font-size-h2: 1.777rem;
41 41 --font-size-h1: 2.369rem;
@@ -50,7 +50,7 @@
50 50 html[data-theme="dark"] {
51 51 color-scheme: dark;
52 52 /* The dark inversion of the same warm system: near-white text on a warm
53 Removed: near-black, with the oxblood primary and a warm amber secondary lightened
53 Added: near-black. The oxblood primary and a warm amber secondary are lightened
54 54 for AA+ contrast on the dark surface. */
55 55 --paper: #141312;
56 56 --paper-raised: #201e1b;
@@ -106,14 +106,19 @@
106 106 outline-offset: 2px;
107 107 }
108 108
109 Added: /* Every <summary> in the app is a custom control: a menu pill or an accordion
110 Added: bar. Strip the native disclosure marker globally, not for each component. */
111 Added: summary::-webkit-details-marker { display: none; }
112 Added: summary::marker { content: ""; }
113 Added:
109 114 .app-shell {
110 115 width: min(100%, 45rem);
111 116 margin: 0 auto;
112 117 padding: 0 1rem 2.5rem;
113 118 }
114 119
115 Removed: /* The oxblood bar is the header's base edge — the brand marker — so it belongs
116 Removed: to the masthead, not the content. */
120 Added: /* The oxblood bar is the header's base edge and the brand marker. It belongs
121 Added: to the masthead, not to the content. */
117 122 .masthead {
118 123 display: flex;
119 124 align-items: center;
@@ -138,8 +143,8 @@
138 143 font: inherit;
139 144 }
140 145
141 Removed: /* Brand stays left; the primary nav and the sign-out control group together
142 Removed: flush to the right, sharing the masthead's vertical centering. */
146 Added: /* The brand stays left. The primary navigation and the sign-out control group
147 Added: together at the right edge. Both share the masthead's vertical centering. */
143 148 .masthead .brand { margin-right: auto; }
144 149
145 150 .brand {
@@ -151,9 +156,9 @@
151 156 text-decoration: none;
152 157 }
153 158
154 Removed: /* The profile control: the signed-in username, linking to the logbook. Right
155 Removed: of the brand, it is the mobile top-bar profile button and joins the inline
156 Removed: nav at desktop widths. It reads as a compact pill. */
159 Added: /* The profile control shows the signed-in username and links to the logbook.
160 Added: It sits right of the brand. On mobile it is the top-bar profile button. At
161 Added: desktop widths it joins the inline navigation. It reads as a compact pill. */
157 162 .profile {
158 163 display: inline-flex;
159 164 align-items: center;
@@ -205,10 +210,17 @@
205 210 color: var(--oxblood-dark);
206 211 }
207 212
213 Added: /* The current page's navigation link fills solid. The inline navigation serves
214 Added: desktop and the bottom bar serves mobile, so both carry the same active
215 Added: treatment. */
208 216 .page-home .primary-nav > a[href="/"],
209 217 .page-routine .primary-nav > a[href="/routine"],
210 218 .page-workout .primary-nav > a[href="/workout"],
211 Removed: .page-logbook .primary-nav > a[href="/logbook"] {
219 Added: .page-logbook .primary-nav > a[href="/logbook"],
220 Added: .page-home .bottom-nav > a[href="/"],
221 Added: .page-routine .bottom-nav > a[href="/routine"],
222 Added: .page-workout .bottom-nav > a[href="/workout"],
223 Added: .page-logbook .bottom-nav > a[href="/logbook"] {
212 224 color: var(--on-primary);
213 225 background: var(--oxblood);
214 226 }
@@ -217,10 +229,10 @@
217 229 font-weight: 800;
218 230 }
219 231
220 Removed: /* The profile drop-down. The <summary> reuses the .profile pill styling; the
221 Removed: panel opens below it with account actions. A <details> menu needs no script,
222 Removed: so it works before the SPA enhancement loads. The open animation is clamped
223 Removed: away for reduced-motion users by the global rule. */
232 Added: /* The profile drop-down. The <summary> reuses the .profile pill styling. The
233 Added: panel opens below the pill and holds the account actions. A <details> menu
234 Added: needs no script, so it works before the SPA enhancement loads. The global
235 Added: reduced-motion rule removes the open animation. */
224 236 .profile-menu {
225 237 position: relative;
226 238 display: inline-block;
@@ -229,8 +241,6 @@
229 241 list-style: none;
230 242 cursor: pointer;
231 243 }
232 Removed: .profile-menu > summary::-webkit-details-marker { display: none; }
233 Removed: .profile-menu > summary::marker { content: ""; }
234 244 .profile-menu-drawer {
235 245 position: absolute;
236 246 right: 0;
@@ -283,14 +293,6 @@
283 293 margin: 0;
284 294 }
285 295
286 Removed: .page-home .bottom-nav > a[href="/"],
287 Removed: .page-routine .bottom-nav > a[href="/routine"],
288 Removed: .page-workout .bottom-nav > a[href="/workout"],
289 Removed: .page-logbook .bottom-nav > a[href="/logbook"] {
290 Removed: color: var(--on-primary);
291 Removed: background: var(--oxblood);
292 Removed: }
293 Removed:
294 296 /* Masthead padding creates the separation from the page card. Keep only the
295 297 bottom padding here so the footer still clears the content. */
296 298 main { padding: 0 0 1.5rem; }
@@ -305,10 +307,10 @@
305 307 padding: 1.25rem 0 0;
306 308 }
307 309
308 Removed: /* Page transitions. The SPA client swaps the content node on navigation
309 Removed: between Home, Current Workout, and Logbook; a freshly inserted node runs this
310 Removed: fade-and-rise, so each transition animates without script. Reduced-motion
311 Removed: users get it instantly, via the global clamp. */
310 Added: /* Page transitions. The SPA client swaps the content node when the user moves
311 Added: between Home, Current Workout, and Logbook. A freshly inserted node runs
312 Added: this fade-and-rise, so each transition animates without script. The global
313 Added: reduced-motion rule shows the content immediately instead. */
312 314 @keyframes page-enter {
313 315 from { opacity: 0; transform: translateY(0.5rem); }
314 316 to { opacity: 1; transform: translateY(0); }
@@ -318,12 +320,12 @@
318 320 animation: page-enter 220ms ease both;
319 321 }
320 322
321 Removed: /* Keep the desktop masthead and navigation wide, while giving reading content
322 Removed: a shorter line length. The raised card returns at this width; mobile stays
323 Removed: flat and native above. */
324 Removed: /* On desktop the shell and content settle at a compact, readable ~50rem, rather
325 Removed: than stretching with the viewport. The shell frames the masthead, content,
326 Removed: and footer; the content fills it. */
323 Added: /* Keep the desktop masthead and navigation wide, and give reading content a
324 Added: shorter line length. The raised card returns at this width. Mobile stays
325 Added: flat and native. */
326 Added: /* On desktop the shell and content settle at a compact, readable 50rem. They
327 Added: do not stretch with the viewport. The shell frames the masthead, content,
328 Added: and footer. The content fills the shell. */
327 329 @media (min-width: 42.0625rem) {
328 330 .app-shell {
329 331 width: min(100%, 50rem);
@@ -335,9 +337,9 @@
335 337 }
336 338
337 339 /* One coherent heading scale across the app. h1 is the page title, h2 a
338 Removed: section within the page, h3 a sub-section. They share colour, weight, and
339 Removed: rhythm; only the size steps down, so the hierarchy reads the same on every
340 Removed: page. */
340 Added: section within the page, and h3 a sub-section. They share colour, weight,
341 Added: and rhythm. Only the size steps down, so the hierarchy reads the same on
342 Added: every page. */
341 343 h1,
342 344 h2,
343 345 h3 {
@@ -347,7 +349,8 @@
347 349 font-weight: 700;
348 350 letter-spacing: -0.03em;
349 351 }
350 Removed: /* Line-height tightens as size grows, paired to the golden scale. */
352 Added: /* Line-height tightens as the size grows, paired to the perfect-fourth
353 Added: scale. */
351 354 h1 {
352 355 font-size: clamp(1.75rem, 4vw, var(--font-size-h1));
353 356 line-height: 1.15;
@@ -365,8 +368,8 @@
365 368 h2 + p { font-size: var(--font-size-lead); }
366 369
367 370
368 Removed: /* The upcoming workout's exercises, previewed on the Home card. A tight ledger
369 Removed: of what is coming, so the next session reads at a glance. */
371 Added: /* The upcoming workout's exercises, previewed on the Home card. A tight
372 Added: ledger of the next session, readable at a glance. */
370 373 .next-workout-preview {
371 374 margin: 0.5rem 0 1rem;
372 375 padding-left: 1.1rem;
@@ -380,9 +383,9 @@
380 383 border: 0;
381 384 }
382 385
383 Removed: /* Base vertical rhythm between fields. Forms with no other layout (auth,
384 Removed: settings) get room to breathe; grids that need a tighter, custom gap
385 Removed: (logging, the set ending) override this per field. */
386 Added: /* Base vertical rhythm between fields. Forms with no other layout, such as
387 Added: auth and settings, get generous spacing. Grids that need a tighter custom
388 Added: gap, such as logging and the set ending, override this for each field. */
386 389 .field {
387 390 margin: 0 0 1rem;
388 391 }
@@ -414,9 +417,9 @@
414 417 padding: 0.65rem 0.75rem;
415 418 }
416 419
417 Removed: /* Dropdowns match the UI: no native chrome, a themed chevron drawn on the
418 Removed: right, and room for it so the text never overlaps. The chevron uses the rule
419 Removed: colour to stay quiet next to the text. */
420 Added: /* Dropdowns match the UI: no native chrome, and a themed chevron drawn on the
421 Added: right. The rule reserves room for the chevron, so the text never overlaps
422 Added: it. The chevron uses the rule colour to stay quiet beside the text. */
420 423 select {
421 424 appearance: none;
422 425 -webkit-appearance: none;
@@ -460,12 +463,12 @@
460 463 cursor: not-allowed;
461 464 }
462 465
463 Removed: /* Correction actions (Save correction + Reset) appear only once a recorded
464 Removed: value has changed. With JavaScript, the pair is hidden until the client marks
465 Removed: the form dirty; the reset button then restores the recorded values, which
466 Removed: clears the dirty state and hides the pair again. Without JavaScript the pair
467 Removed: stays visible, so a correction is always possible. The buttons share the row
468 Removed: at equal width. */
466 Added: /* The correction actions, Save correction and Reset, appear only after a
467 Added: recorded value changes. With JavaScript, the pair stays hidden until the
468 Added: client marks the form dirty. The reset button then restores the recorded
469 Added: values. That clears the dirty state and hides the pair again. Without
470 Added: JavaScript the pair stays visible, so a correction is always possible. The
471 Added: buttons share the row at equal width. */
469 472 .correction-actions {
470 473 display: grid;
471 474 grid-template-columns: 1fr 1fr;
@@ -479,11 +482,12 @@
479 482 .correction-actions[data-hito-correction-actions] {
480 483 display: grid;
481 484 }
482 Removed: /* Save correction reads as a primary outline: oxblood border and text on a
483 Removed: transparent field, filling oxblood on hover. It pairs with the Reset button
484 Removed: beside it without competing as a solid fill. The selector names both group
485 Removed: classes so it beats the generic .button-group solid-fill rule, which ties on
486 Removed: specificity and would otherwise win by order. */
485 Added: /* Save correction reads as a primary outline: an oxblood border and oxblood
486 Added: text on a transparent field, which fills oxblood on hover. It pairs with the
487 Added: Reset button beside it and does not compete as a solid fill. The selector
488 Added: names both group classes. It therefore beats the generic .button-group
489 Added: solid-fill rule, which matches the same specificity and would otherwise win
490 Added: by order. */
487 491 .button-group.correction-actions input[type="submit"] {
488 492 color: var(--oxblood);
489 493 background: transparent;
@@ -493,8 +497,9 @@
493 497 color: var(--on-primary);
494 498 background: var(--oxblood);
495 499 }
496 Removed: /* Both correction controls sit in one grid row, so neither carries its own top
497 Removed: margin — the group's top margin is the single source of vertical spacing. */
500 Added: /* Both correction controls sit in one grid row, so neither carries its own
501 Added: top margin. The group's top margin is the single source of vertical
502 Added: spacing. */
498 503 .button-group.correction-actions input[type="submit"],
499 504 .button-group.correction-actions button[type="reset"].secondary {
500 505 margin-top: 0;
@@ -581,9 +586,10 @@
581 586 background: var(--oxblood);
582 587 }
583 588
584 Removed: /* Every control in a button group — an anchor, a form button, or a submit
585 Removed: input — shares the primary look, so modals and action rows read alike. The
586 Removed: group is a two-column grid: primary action left, secondary right. */
589 Added: /* A button group can hold an anchor, a form button, or a submit input. Every
590 Added: one of them shares the primary look, so modals and action rows read alike.
591 Added: The group is a two-column grid: the primary action left, the secondary
592 Added: right. */
587 593 .button-group button,
588 594 .button-group input[type="submit"] {
589 595 display: inline-flex;
@@ -615,11 +621,12 @@
615 621 background: var(--brass);
616 622 }
617 623
618 Removed: /* The finish and cancel actions signal readiness by their fill. While any slot
619 Removed: is still unrecorded, both read as a primary outline — present but not urging
620 Removed: completion. Once every slot is recorded, both fill solid oxblood to invite
621 Removed: the finish. The selectors name both group classes so they beat the generic
622 Removed: solid-fill and the .secondary brass outline, which tie on specificity. */
624 Added: /* The finish and cancel actions signal readiness by their fill. While any
625 Added: slot is still unrecorded, both read as a primary outline. They are present,
626 Added: but they do not urge completion. Once every slot is recorded, both fill
627 Added: solid oxblood to invite the finish. The selectors name both group classes,
628 Added: so they beat the generic solid-fill rule and the .secondary brass outline,
629 Added: which match the same specificity. */
623 630 .button-group.finish-actions input[type="submit"],
624 631 .button-group.finish-actions input[type="submit"].secondary {
625 632 color: var(--oxblood);
@@ -642,10 +649,10 @@
642 649 background: var(--oxblood-hover);
643 650 }
644 651
645 Removed: /* The logbook is one real table at every width. The columns — date, workout
646 Removed: name, duration, and the row menu — are few and narrow enough to share a
647 Removed: single table layout, so there is no mobile card reflow: the name column takes
648 Removed: the slack and truncates when space is tight. */
652 Added: /* The logbook is one real table at every width. The columns are date, workout
653 Added: name, duration, and the row menu. They are few and narrow enough to share a
654 Added: single table layout, so there is no mobile card reflow. The name column
655 Added: absorbs the spare width and truncates when space is tight. */
649 656 .logbook-table {
650 657 width: 100%;
651 658 margin-top: 1.5rem;
@@ -665,9 +672,9 @@
665 672 letter-spacing: 0.04em;
666 673 text-transform: uppercase;
667 674 }
668 Removed: /* The columns are narrow enough to share one table layout at every width:
669 Removed: monospaced date, sans workout name that takes the slack and truncates,
670 Removed: monospaced duration, and the three-dot menu. No mobile card reflow. */
675 Added: /* The treatment for each column: a monospaced date, a sans workout name that
676 Added: absorbs the spare width and truncates, a monospaced duration, and the
677 Added: three-dot menu. */
671 678 .logbook-table td:nth-child(1) {
672 679 font-family: var(--mono);
673 680 white-space: nowrap;
@@ -707,8 +714,8 @@
707 714 }
708 715 .logbook-no-progress { color: var(--muted-ink); }
709 716
710 Removed: /* One native <details> action menu per row. The dots keep the ledger compact;
711 Removed: the drawer carries destructive work outside the evidence fields. */
717 Added: /* One native <details> action menu per row. The dots keep the ledger compact.
718 Added: The drawer holds the destructive actions outside the evidence fields. */
712 719 .logbook-actions-heading,
713 720 .logbook-actions {
714 721 width: 2.75rem;
@@ -730,8 +737,6 @@
730 737 font-size: 1.35rem;
731 738 line-height: 1;
732 739 }
733 Removed: .logbook-row-menu > summary::-webkit-details-marker { display: none; }
734 Removed: .logbook-row-menu > summary::marker { content: ""; }
735 740 .logbook-row-menu > summary:hover,
736 741 .logbook-row-menu[open] > summary { background: var(--brass-wash); }
737 742 .logbook-row-menu-drawer {
@@ -766,8 +771,9 @@
766 771 background: var(--oxblood);
767 772 }
768 773
769 Removed: /* The imported workout's menu drawer holds an exercise/progress list rather
770 Removed: than action links, so it is wider and its items are read, not clicked. */
774 Added: /* The imported workout's menu drawer holds an exercise and progress list
775 Added: instead of action links. The drawer is therefore wider, and a reader reads
776 Added: its items instead of clicking them. */
771 777 .logbook-import-drawer {
772 778 min-width: 15rem;
773 779 max-width: min(22rem, 80vw);
@@ -775,8 +781,8 @@
775 781 text-align: left;
776 782 }
777 783
778 Removed: /* An import is marked by an oxblood outline "Imported" pill, tucked into the
779 Removed: top of its three-dot menu drawer rather than shown beside the title. */
784 Added: /* An oxblood outline "Imported" pill marks an import. The pill sits at the
785 Added: top of the three-dot menu drawer, not beside the title. */
780 786 .logbook-import-pill-row {
781 787 margin: 0 0 0.5rem;
782 788 }
@@ -830,10 +836,10 @@
830 836 /* The action bar under the logbook heading. */
831 837 .logbook-actions-bar { margin: 0.5rem 0 1rem; }
832 838
833 Removed: /* The month calendar above the logbook: a self-contained widget, laid out flat
834 Removed: on the page. A header row (prev arrow, month/year picker, next arrow), a
835 Removed: Today link, and the seven-column day grid. Days with a workout are filled
836 Removed: oxblood; today is ringed. */
839 Added: /* The month calendar above the logbook is a self-contained widget, flat on
840 Added: the page. A header row holds a previous arrow, a month and year picker, and
841 Added: a next arrow. Below it sit a Today link and the seven-column day grid. Days
842 Added: with a workout are filled oxblood. Today is ringed. */
837 843 .calendar {
838 844 max-width: 22rem;
839 845 margin: 0 auto 1.5rem;
@@ -947,7 +953,7 @@
947 953 border-top-style: dashed;
948 954 }
949 955
950 Removed: /* The chart scales to its container; the viewBox does the layout. */
956 Added: /* The chart scales to its container. The viewBox does the layout. */
951 957 .progress-chart {
952 958 display: block;
953 959 width: 100%;
@@ -956,10 +962,11 @@
956 962 }
957 963 .chart-axis { stroke: var(--rule-strong); stroke-width: 1; }
958 964 .chart-grid { stroke: var(--rule); stroke-width: 1; opacity: 0.5; }
959 Removed: /* Chart text is sized in viewBox user units, not rem: the SVG scales to the
960 Removed: container, so rem labels shrank to ~half size on a phone. User-unit sizes
961 Removed: track the 640-unit viewBox, staying legible when the chart is narrow. At the
962 Removed: 40rem cap they render ~20px; on a ~375px phone (≈0.59 scale) ~12px. */
965 Added: /* Chart text uses viewBox user units, not rem. The SVG scales to its
966 Added: container, so rem labels fell to about half size on a phone. User-unit sizes
967 Added: track the 640-unit viewBox and stay legible when the chart is narrow. At the
968 Added: 40rem cap they render about 20px. On a 375px phone, at about 0.59 scale,
969 Added: they render about 12px. */
963 970 .chart-tick { font: 500 20px var(--mono); fill: var(--muted-ink); }
964 971 .chart-axis-title { font: 700 22px var(--sans); }
965 972 .chart-axis-title.weight { fill: var(--oxblood-dark); }
@@ -971,12 +978,12 @@
971 978 .chart-series.reps { stroke: var(--brass); stroke-dasharray: 5 3; }
972 979
973 980 /* --- Workout logging fieldset ---
974 Removed: The set is a grid of movement groups so the three columns hold the golden
975 Removed: ratio: the exercise name is the smaller part (1fr); load and reps take the
976 Removed: wider φ portion, split evenly (0.809fr each). Each movement group and the
977 Removed: heading row are display:contents so their cells fall on the grid. The set
978 Removed: ending (extension select + optional amount) is a separate row below the
979 Removed: grid. *)
981 Added: The set is a grid of movement groups, so the three columns hold the golden
982 Added: ratio. The exercise name is the smaller part at 1fr. Load and reps take the
983 Added: wider portion, split evenly at 0.809fr each. Each movement group and the
984 Added: heading row use display:contents, so their cells land on the grid. The set
985 Added: ending, which is the extension select and an optional amount, is a separate
986 Added: row below the grid. */
980 987 .exercise-name-label {
981 988 display: block;
982 989 color: var(--ink);
@@ -995,9 +1002,9 @@
995 1002
996 1003 .logging-grid {
997 1004 display: grid;
998 Removed: /* Golden ratio between the name column and the two number columns: name is
999 Removed: the smaller part (1), load and reps together are the larger part (φ ≈
1000 Removed: 1.618), split evenly (0.809fr each). So name : (load + reps) = 1 : φ. */
1005 Added: /* The golden ratio separates the name column from the two number columns.
1006 Added: The name is the smaller part at 1. Load and reps together are the larger
1007 Added: part, about 1.618, split evenly at 0.809fr each. */
1001 1008 grid-template-columns: minmax(0, 1fr) minmax(0, 0.809fr) minmax(0, 0.809fr);
1002 1009 align-items: center;
1003 1010 gap: 0.35rem 0.6rem;
@@ -1009,8 +1016,8 @@
1009 1016 .movement {
1010 1017 display: contents;
1011 1018 }
1012 Removed: /* The two visible column headings sit over the number columns; the name column
1013 Removed: has no heading (the names are self-describing). */
1019 Added: /* The two visible column headings sit over the number columns. The name
1020 Added: column has no heading, because the names describe themselves. */
1014 1021 .logging-grid-head-load,
1015 1022 .logging-grid-head-reps {
1016 1023 padding-bottom: 0.35rem;
@@ -1021,10 +1028,10 @@
1021 1028 .logging-grid .field-number { padding: 0; }
1022 1029 .logging-grid input { width: 100%; }
1023 1030
1024 Removed: /* The set ending sits under the number columns and shares the logging grid's
1025 Removed: geometry, so the select's left edge lines up with the Load column and the
1026 Removed: amount lines up with the Reps column. The name column stays empty. The select
1027 Removed: fills its column; the amount is a compact input followed by a muted unit. */
1031 Added: /* The set ending sits below the number columns and shares the logging grid's
1032 Added: geometry. The select's left edge aligns with the Load column, and the amount
1033 Added: aligns with the Reps column. The name column stays empty. The select fills
1034 Added: its column. The amount is a compact input with a muted unit after it. */
1028 1035 .ending-block {
1029 1036 display: grid;
1030 1037 grid-template-columns: minmax(0, 1fr) minmax(0, 0.809fr) minmax(0, 0.809fr);
@@ -1033,9 +1040,10 @@
1033 1040 margin-top: 0.6rem;
1034 1041 }
1035 1042 .ending-block .field { margin: 0; min-width: 0; }
1036 Removed: /* The select sits in the Load column and the amount in the Reps column, so both
1037 Removed: align with the inputs above. At positive failure the amount is hidden, so the
1038 Removed: select then spans both number columns to fill that width (see the :has block). */
1043 Added: /* The select sits in the Load column and the amount in the Reps column, so
1044 Added: both align with the inputs above. Positive failure hides the amount. The
1045 Added: select then spans both number columns to fill that width. See the :has
1046 Added: block. */
1039 1047 .ending-block .field-ending { grid-column: 2; }
1040 1048 .ending-block .field-extension-amount { grid-column: 3; }
1041 1049 /* Input and its unit stay together on the amount line, and never wrap onto
@@ -1056,9 +1064,9 @@
1056 1064 font: 500 var(--font-size-small)/1 var(--sans);
1057 1065 }
1058 1066
1059 Removed: /* The extension amount follows the live selection with no script: :has() reads
1060 Removed: the checked option. Positive failure hides the amount; a static hold counts
1061 Removed: seconds, everything else counts reps. */
1067 Added: /* The extension amount follows the live selection with no script, because
1068 Added: :has() reads the checked option. Positive failure hides the amount. A static
1069 Added: hold counts seconds. Every other ending counts reps. */
1062 1070 @supports selector(:has(*)) {
1063 1071 .logging-fieldset .amount-label-sep { display: none; }
1064 1072 .logging-fieldset:has(select[name="extension"] option[value=""]:checked)
@@ -1095,9 +1103,9 @@
1095 1103 border: 0;
1096 1104 }
1097 1105
1098 Removed: /* The Home affordance shown while a workout is in progress: a full-width
1099 Removed: pressable panel that returns the user to the current workout. A left oxblood
1100 Removed: accent marks it; it is not the page-surface card metaphor. */
1106 Added: /* Home shows this affordance while a workout is in progress. It is a
1107 Added: full-width pressable panel that returns the user to the current workout. A
1108 Added: left oxblood accent marks the panel. It is not the page-surface card. */
1101 1109 .in-progress {
1102 1110 display: block;
1103 1111 margin: 1.25rem 0 0;
@@ -1115,10 +1123,11 @@
1115 1123 .in-progress h1 { margin: 0.35rem 0; }
1116 1124 .in-progress p { margin: 0; }
1117 1125
1118 Removed: /* The exercise accordion: one <details> per prescription slot. They share a
1119 Removed: [name], so the browser keeps exactly one open — no script. The summary holds
1120 Removed: the slot's level-two heading (number + exercise label, struck through once
1121 Removed: recorded) and a +/− marker. A recorded slot gets a brass left accent. */
1126 Added: /* The exercise accordion uses one <details> per prescription slot. They share
1127 Added: a [name], so the browser keeps exactly one open without script. The summary
1128 Added: holds the slot's level-two heading and a plus or minus marker. The heading
1129 Added: is the slot number and the exercise label, struck through once recorded. A
1130 Added: recorded slot gets a brass left accent. */
1122 1131 .slot-accordions {
1123 1132 display: grid;
1124 1133 gap: 0.65rem;
@@ -1139,17 +1148,15 @@
1139 1148 cursor: pointer;
1140 1149 list-style: none;
1141 1150 }
1142 Removed: .slot-accordion > summary::-webkit-details-marker { display: none; }
1143 Removed: .slot-accordion > summary::marker { content: ""; }
1144 1151 .slot-accordion > summary::after {
1145 1152 margin-left: auto;
1146 1153 content: "+";
1147 1154 color: var(--muted-ink);
1148 1155 }
1149 1156 .slot-accordion[open] > summary::after { content: "−"; }
1150 Removed: /* The summary's level-two heading lays out inline: number, then label. It keeps
1151 Removed: the global h2 size and only resets the default heading margin so it sits
1152 Removed: centered in the summary bar. */
1157 Added: /* The summary's level-two heading is inline: the number, then the label. It
1158 Added: keeps the global h2 size. It resets only the default heading margin, so the
1159 Added: heading sits centered in the summary bar. */
1153 1160 .slot-heading {
1154 1161 display: flex;
1155 1162 align-items: baseline;
@@ -1165,13 +1172,14 @@
1165 1172 border-left: 4px solid var(--brass);
1166 1173 }
1167 1174
1168 Removed: /* Unfold and collapse. ::details-content is the box the browser toggles for a
1169 Removed: <details>; keep it rendered when closed and drive a grid row from 0fr to 1fr
1170 Removed: off the [open] attribute, so both opening and closing ease in every browser
1171 Removed: that has ::details-content. visibility keeps a closed panel out of the tab
1172 Removed: order and the accessibility tree; it flips to visible at the start of an open
1173 Removed: and to hidden at the end of a close. Browsers without ::details-content
1174 Removed: ignore all of this and snap. Reduced motion snaps too. */
1175 Added: /* Unfold and collapse. ::details-content is the box that the browser toggles
1176 Added: for a <details>. Keep that box rendered when closed. Drive a grid row from
1177 Added: 0fr to 1fr from the [open] attribute, so both opening and closing ease in
1178 Added: every browser that supports ::details-content. The visibility property keeps
1179 Added: a closed panel out of the tab order and the accessibility tree. It changes
1180 Added: to visible when an open starts, and to hidden when a close ends. Browsers
1181 Added: without ::details-content ignore all of this and snap. Reduced motion also
1182 Added: snaps. */
1175 1183 .slot-accordion::details-content {
1176 1184 content-visibility: visible;
1177 1185 display: grid;
@@ -1194,26 +1202,30 @@
1194 1202 .slot-accordion::details-content { transition: none; }
1195 1203 }
1196 1204
1197 Removed: /* The elapsed workout timer lives in the middle nav slot while logging, in
1198 Removed: place of the routine link. It is a nav link like the others, so it inherits
1199 Removed: the nav layout and the active oxblood fill; only its value reads as a
1200 Removed: monospace clock with tabular numerals so the digits do not jitter. */
1205 Added: /* The elapsed workout timer occupies the middle navigation slot while
1206 Added: logging, in place of the routine link. It is a navigation link like the
1207 Added: others, so it inherits the navigation layout and the active oxblood fill.
1208 Added: Only its value differs: a monospace clock with tabular numerals, so the
1209 Added: digits do not jitter. */
1201 1210 .nav-timer-value {
1202 1211 font: 700 var(--font-size-base)/1 var(--mono);
1203 1212 font-variant-numeric: tabular-nums;
1204 1213 letter-spacing: 0.02em;
1205 1214 }
1206 1215
1207 Removed: /* The cancel confirmation modal. A native dialog styled as a raised paper
1208 Removed: surface: the oxblood top accent, radius, border, and shadow echo the page
1209 Removed: surface, so the modal reads as part of the same interface. The actions reuse
1210 Removed: the primary and secondary submit styling, including hover and transition. It
1211 Removed: opens with a short scale-and-fade, clamped for reduced motion. */
1216 Added: /* Every modal is a native dialog styled as a raised paper surface. The oxblood
1217 Added: top accent, radius, border, and shadow echo the page surface. A modal
1218 Added: therefore reads as part of the same interface. The actions reuse the primary
1219 Added: and secondary submit styling, including hover and transition. Each modal
1220 Added: opens with a short scale-and-fade, clamped for reduced motion. Only width and
1221 Added: padding differ between them, set for each dialog below. */
1212 1222 @keyframes modal-enter {
1213 1223 from { opacity: 0; transform: translateY(0.4rem) scale(0.98); }
1214 1224 to { opacity: 1; transform: translateY(0) scale(1); }
1215 1225 }
1216 Removed: .confirm-dialog {
1226 Added: .confirm-dialog,
1227 Added: .feedback-modal,
1228 Added: .profile-dialog {
1217 1229 width: min(100%, 26rem);
1218 1230 margin: auto;
1219 1231 border: 1px solid var(--rule-strong);
@@ -1224,10 +1236,12 @@
1224 1236 padding: clamp(1.25rem, 3vw, 1.75rem);
1225 1237 color: var(--ink);
1226 1238 }
1227 Removed: .confirm-dialog[open] { animation: modal-enter 180ms ease both; }
1228 Removed: .confirm-dialog::backdrop {
1229 Removed: background: rgba(26, 26, 22, 0.45);
1230 Removed: }
1239 Added: .confirm-dialog[open],
1240 Added: .feedback-modal[open],
1241 Added: .profile-dialog[open] { animation: modal-enter 180ms ease both; }
1242 Added: .confirm-dialog::backdrop,
1243 Added: .feedback-modal::backdrop,
1244 Added: .profile-dialog::backdrop { background: rgba(26, 26, 22, 0.45); }
1231 1245 .confirm-dialog button {
1232 1246 width: 100%;
1233 1247 min-height: 3rem;
@@ -1255,20 +1269,12 @@
1255 1269
1256 1270 /* The sequential feedback flow opens in a native modal. The [open] fallback
1257 1271 keeps the flow usable when script is disabled, while the client upgrades it
1258 Removed: to [showModal] and supplies the backdrop. */
1272 Added: to [showModal] and supplies the backdrop. It runs wider than the other
1273 Added: dialogs and lets its content own the padding. */
1259 1274 .feedback-modal {
1260 1275 width: min(calc(100% - 2rem), 34rem);
1261 Removed: margin: auto;
1262 Removed: border: 1px solid var(--rule-strong);
1263 Removed: border-top: 6px solid var(--oxblood);
1264 Removed: border-radius: var(--radius);
1265 Removed: background: var(--paper-raised);
1266 Removed: box-shadow: 0 0.6rem 2rem rgba(26, 26, 22, 0.25);
1267 1276 padding: 0;
1268 Removed: color: var(--ink);
1269 1277 }
1270 Removed: .feedback-modal::backdrop { background: rgba(26, 26, 22, 0.45); }
1271 Removed: .feedback-modal[open] { animation: modal-enter 180ms ease both; }
1272 1278 .feedback-modal-content { padding: 1.25rem; }
1273 1279
1274 1280 .settings-section {
@@ -1336,23 +1342,6 @@
1336 1342 }
1337 1343 .profile-edit:hover { background: var(--brass-wash); }
1338 1344
1339 Removed: /* The profile edit modal reuses the confirm modal's raised-paper look and
1340 Removed: entrance animation, so username and password edits open like every other
1341 Removed: modal. */
1342 Removed: .profile-dialog {
1343 Removed: width: min(100%, 26rem);
1344 Removed: margin: auto;
1345 Removed: border: 1px solid var(--rule-strong);
1346 Removed: border-top: 6px solid var(--oxblood);
1347 Removed: border-radius: var(--radius);
1348 Removed: background: var(--paper-raised);
1349 Removed: box-shadow: 0 0.6rem 2rem rgba(26, 26, 22, 0.25);
1350 Removed: padding: clamp(1.25rem, 3vw, 1.75rem);
1351 Removed: color: var(--ink);
1352 Removed: }
1353 Removed: .profile-dialog[open] { animation: modal-enter 180ms ease both; }
1354 Removed: .profile-dialog::backdrop { background: rgba(26, 26, 22, 0.45); }
1355 Removed:
1356 1345 .done {
1357 1346 color: var(--evergreen);
1358 1347 font: 700 0.85rem/1.4 var(--mono);
@@ -1436,9 +1425,10 @@
1436 1425 }
1437 1426
1438 1427 /* Mobile-first navigation with a single desktop breakpoint at 42.0625rem.
1439 Removed: Below it, the layout is mobile: a fixed bottom nav for the three actions,
1440 Removed: with the sign-out control hidden. At or above it, the inline primary nav
1441 Removed: joins the header sign-out and the bottom nav is hidden. */
1428 Added: Below that width the layout is mobile: a fixed bottom navigation for the
1429 Added: three actions, with the sign-out control hidden. At or above that width the
1430 Added: inline primary navigation joins the header sign-out, and the bottom
1431 Added: navigation is hidden. */
1442 1432 .primary-nav { display: none; }
1443 1433 .bottom-nav {
1444 1434 position: fixed;
@@ -1454,9 +1444,9 @@
1454 1444 box-shadow: 0 -0.35rem 1.2rem rgba(26, 26, 22, 0.15);
1455 1445 padding: 0.5rem 0.6rem calc(0.5rem + env(safe-area-inset-bottom));
1456 1446 }
1457 Removed: /* The tabs are round-corner buttons floating on the paper bar, so mobile and
1458 Removed: desktop activity buttons share one appearance: the same radius, hover, and
1459 Removed: active oxblood fill. The bar is a touch larger than the buttons it holds. */
1447 Added: /* The tabs are round-corner buttons on the paper bar. Mobile and desktop
1448 Added: activity buttons therefore share one appearance: the same radius, hover, and
1449 Added: active oxblood fill. The bar is slightly larger than the buttons it holds. */
1460 1450 .bottom-nav > a {
1461 1451 display: grid;
1462 1452 min-height: 3rem;
@@ -1544,7 +1534,7 @@
1544 1534 }
1545 1535
1546 1536 /* Subjective feedback uses one inline three-choice group per signal. The radio
1547 Removed: input keeps native keyboard and form behavior; its adjacent span is the
1537 Added: input keeps the native keyboard and form behaviour. The adjacent span is the
1548 1538 visible button. */
1549 1539 .feedback-group {
1550 1540 margin: 1rem 0;