bacon-tracker 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1065 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Bacon Tracker - <%= h(@project_name || @namespace) %></title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,300;0,400;1,400&display=swap" rel="stylesheet">
10
+ <script><%= erb :'_theme_boot', layout: false %></script>
11
+ <style>
12
+ <%= erb :'_root_css', layout: false %>
13
+ <%= erb :'_board_css', layout: false %>
14
+
15
+ body {
16
+ font-family: var(--font-mono);
17
+ font-size: 14px;
18
+ background: var(--bg);
19
+ color: var(--fg);
20
+ height: 100dvh;
21
+ display: flex;
22
+ flex-direction: column;
23
+ overflow: hidden;
24
+ }
25
+
26
+ /* ── Header ─────────────────────────────────────────────────── */
27
+
28
+ <%= erb :'_header_css', layout: false %>
29
+
30
+ .reveal-link {
31
+ background: none;
32
+ border: 1px solid rgba(255, 227, 240, 0.18);
33
+ color: rgba(255, 227, 240, 0.4);
34
+ font-family: var(--font-mono);
35
+ font-size: 0.7rem;
36
+ padding: 2px 8px;
37
+ border-radius: var(--radius-sm);
38
+ cursor: pointer;
39
+ transition:
40
+ color 0.12s,
41
+ border-color 0.12s;
42
+ }
43
+ .reveal-link:hover {
44
+ color: var(--color-pink);
45
+ border-color: rgba(255, 227, 240, 0.5);
46
+ }
47
+
48
+ /* ── Next bar ────────────────────────────────────────────────── */
49
+
50
+ .next-bar {
51
+ background: rgba(192, 130, 162, 0.1);
52
+ border-bottom: 1px solid rgba(192, 130, 162, 0.22);
53
+ padding: 6px 14px;
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 8px;
57
+ font-size: 0.73rem;
58
+ flex-shrink: 0;
59
+ }
60
+ .next-label {
61
+ color: var(--accent);
62
+ font-size: 0.62rem;
63
+ letter-spacing: 0.05em;
64
+ flex-shrink: 0;
65
+ }
66
+ .next-title {
67
+ color: var(--fg-strong);
68
+ overflow: hidden;
69
+ text-overflow: ellipsis;
70
+ white-space: nowrap;
71
+ flex: 1;
72
+ }
73
+
74
+ .load-error {
75
+ background: rgba(212, 112, 112, 0.12);
76
+ border-bottom: 1px solid rgba(212, 112, 112, 0.3);
77
+ padding: 6px 14px;
78
+ display: flex;
79
+ align-items: center;
80
+ gap: 8px;
81
+ font-size: 0.73rem;
82
+ color: var(--c-bug);
83
+ flex-shrink: 0;
84
+ }
85
+ .load-error button {
86
+ font-family: var(--font-mono);
87
+ font-size: 0.68rem;
88
+ color: var(--c-bug);
89
+ background: none;
90
+ border: 1px solid rgba(212, 112, 112, 0.4);
91
+ border-radius: var(--radius-input);
92
+ padding: 1px 8px;
93
+ cursor: pointer;
94
+ }
95
+ .load-error button:hover {
96
+ background: rgba(212, 112, 112, 0.12);
97
+ }
98
+
99
+ /* ── Board ───────────────────────────────────────────────────── */
100
+
101
+ .column[data-stage="1_icebox"] {
102
+ background: rgba(107, 144, 160, 0.09);
103
+ border-color: rgba(107, 144, 160, 0.2);
104
+ }
105
+ .column[data-stage="2_backlog"] {
106
+ background: rgba(192, 130, 162, 0.1);
107
+ border-color: rgba(192, 130, 162, 0.22);
108
+ }
109
+ .column[data-stage="3_started"] {
110
+ background: rgba(80, 140, 100, 0.08);
111
+ border-color: rgba(80, 140, 100, 0.18);
112
+ }
113
+ .column[data-stage="4_done"] {
114
+ background: rgba(195, 195, 195, 0.1);
115
+ border-color: rgba(195, 195, 195, 0.22);
116
+ }
117
+ .column.drop-target {
118
+ background: rgba(192, 130, 162, 0.2) !important;
119
+ border-color: var(--accent) !important;
120
+ }
121
+
122
+ /* Collapse and pagination are column mechanics, not done-column mechanics -
123
+ the decisions board reuses them on its historical status columns
124
+ (BT-137/BT-144). Only the client decides which columns get the class;
125
+ today that is 4_done alone, so this de-scope changes nothing visible. */
126
+
127
+ /* ── Col header ──────────────────────────────────────────────── */
128
+
129
+ .column[data-stage="1_icebox"] .col-header h2 {
130
+ color: var(--c-chore);
131
+ }
132
+ .column[data-stage="2_backlog"] .col-header h2 {
133
+ color: var(--accent);
134
+ }
135
+ .column[data-stage="3_started"] .col-header h2 {
136
+ color: #5e8c6a;
137
+ }
138
+ .column[data-stage="4_done"] .col-header h2 {
139
+ color: var(--fg-muted);
140
+ }
141
+
142
+ .column[data-stage="1_icebox"] .col-sublabel {
143
+ color: var(--c-chore);
144
+ }
145
+ .column[data-stage="2_backlog"] .col-sublabel {
146
+ color: var(--accent);
147
+ }
148
+
149
+ .col-right {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 6px;
153
+ }
154
+
155
+ .count {
156
+ font-size: 0.62rem;
157
+ color: var(--fg-muted);
158
+ background: rgba(192, 130, 162, 0.12);
159
+ padding: 1px 6px;
160
+ border-radius: 10px;
161
+ }
162
+
163
+ .btn-add,
164
+ .btn-collapse {
165
+ background: none;
166
+ border: 1px solid rgba(195, 195, 195, 0.65);
167
+ color: var(--fg-muted);
168
+ width: 20px;
169
+ height: 20px;
170
+ border-radius: var(--radius-sm);
171
+ cursor: pointer;
172
+ font-size: 0.8rem;
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ transition:
177
+ color 0.12s,
178
+ border-color 0.12s;
179
+ }
180
+ .btn-add:hover,
181
+ .btn-collapse:hover {
182
+ color: var(--accent);
183
+ border-color: var(--accent);
184
+ }
185
+
186
+ /* ── Create form ─────────────────────────────────────────────── */
187
+
188
+ .create-form {
189
+ display: none;
190
+ flex-direction: column;
191
+ gap: 6px;
192
+ padding: 8px 10px;
193
+ background: rgba(255, 255, 255, 0.55);
194
+ border-bottom: 1px solid rgba(192, 130, 162, 0.15);
195
+ flex-shrink: 0;
196
+ }
197
+
198
+ .create-type-row {
199
+ display: flex;
200
+ gap: 4px;
201
+ }
202
+
203
+ .type-btn {
204
+ font-family: var(--font-mono);
205
+ font-size: 0.6rem;
206
+ letter-spacing: 0.02em;
207
+ padding: 2px 7px;
208
+ border-radius: 3px;
209
+ border: 1px solid var(--border);
210
+ background: none;
211
+ color: var(--fg-muted);
212
+ cursor: pointer;
213
+ transition: all 0.12s;
214
+ }
215
+ .type-btn:hover {
216
+ color: var(--fg);
217
+ border-color: rgba(195, 195, 195, 0.9);
218
+ }
219
+ .type-btn.active[data-type="feature"] {
220
+ background: rgba(192, 130, 162, 0.13);
221
+ color: var(--c-feature);
222
+ border-color: rgba(192, 130, 162, 0.4);
223
+ }
224
+ .type-btn.active[data-type="bug"] {
225
+ background: rgba(212, 112, 112, 0.1);
226
+ color: var(--c-bug);
227
+ border-color: rgba(212, 112, 112, 0.35);
228
+ }
229
+ .type-btn.active[data-type="chore"] {
230
+ background: rgba(107, 144, 160, 0.12);
231
+ color: var(--c-chore);
232
+ border-color: rgba(107, 144, 160, 0.35);
233
+ }
234
+
235
+ .create-input-row {
236
+ display: flex;
237
+ gap: 5px;
238
+ }
239
+
240
+ .create-title {
241
+ flex: 1;
242
+ font-family: var(--font-mono);
243
+ font-size: 0.78rem;
244
+ border: 1px solid var(--border);
245
+ border-radius: var(--radius-input);
246
+ padding: 5px 8px;
247
+ color: var(--fg-strong);
248
+ background: #fff;
249
+ outline: none;
250
+ }
251
+ .create-title:focus {
252
+ border-color: var(--accent);
253
+ }
254
+ .create-title::placeholder {
255
+ color: var(--fg-muted);
256
+ }
257
+
258
+ .btn-create-submit {
259
+ font-family: var(--font-mono);
260
+ font-size: 0.65rem;
261
+ padding: 4px 10px;
262
+ border-radius: 4px;
263
+ cursor: pointer;
264
+ background: var(--accent);
265
+ color: #fff;
266
+ border: 1px solid var(--accent);
267
+ transition: opacity 0.12s;
268
+ }
269
+ .btn-create-submit:hover {
270
+ opacity: 0.85;
271
+ }
272
+
273
+ .btn-create-cancel {
274
+ font-family: var(--font-mono);
275
+ font-size: 0.65rem;
276
+ padding: 4px 7px;
277
+ border-radius: 4px;
278
+ cursor: pointer;
279
+ background: none;
280
+ color: var(--fg-muted);
281
+ border: 1px solid transparent;
282
+ }
283
+ .btn-create-cancel:hover {
284
+ color: var(--fg);
285
+ }
286
+
287
+ /* ── Cards list ──────────────────────────────────────────────── */
288
+
289
+ .empty-state {
290
+ flex: 1;
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: center;
294
+ padding: 24px 12px;
295
+ }
296
+ .empty-state p {
297
+ font-size: 0.68rem;
298
+ color: var(--fg-muted);
299
+ line-height: 1.65;
300
+ text-align: center;
301
+ opacity: 0.75;
302
+ }
303
+ .empty-state .hint {
304
+ font-size: 0.61rem;
305
+ margin-top: 3px;
306
+ opacity: 0.6;
307
+ }
308
+
309
+ /* ── Card ────────────────────────────────────────────────────── */
310
+
311
+ .card.dragging {
312
+ opacity: 0;
313
+ height: 0 !important;
314
+ overflow: hidden;
315
+ padding: 0 !important;
316
+ margin: 0 !important;
317
+ border: none !important;
318
+ }
319
+ .card.editing {
320
+ cursor: default;
321
+ box-shadow: 0 4px 14px rgba(192, 130, 162, 0.22);
322
+ }
323
+ .card.flash {
324
+ animation: card-flash 1.4s ease-out;
325
+ }
326
+ @keyframes card-flash {
327
+ 0%,
328
+ 30% {
329
+ box-shadow:
330
+ 0 0 0 2px var(--accent),
331
+ 0 4px 14px rgba(192, 130, 162, 0.35);
332
+ }
333
+ 100% {
334
+ box-shadow: 0 0 0 0 transparent;
335
+ }
336
+ }
337
+
338
+ .card.type-feature {
339
+ border-left-color: var(--c-feature);
340
+ }
341
+ .card.type-bug {
342
+ border-left-color: var(--c-bug);
343
+ }
344
+ .card.type-chore {
345
+ border-left-color: var(--c-chore);
346
+ }
347
+
348
+ /* Card view */
349
+ .card-view {
350
+ padding: 8px 10px;
351
+ }
352
+
353
+ .card-header {
354
+ display: flex;
355
+ align-items: center;
356
+ gap: 5px;
357
+ margin-bottom: 4px;
358
+ flex-wrap: wrap;
359
+ }
360
+
361
+ .badge {
362
+ font-size: 0.55rem;
363
+ letter-spacing: 0.06em;
364
+ text-transform: uppercase;
365
+ padding: 1px 5px;
366
+ border-radius: 3px;
367
+ }
368
+ .badge.feature {
369
+ background: rgba(192, 130, 162, 0.12);
370
+ color: var(--c-feature);
371
+ }
372
+ .badge.bug {
373
+ background: rgba(212, 112, 112, 0.1);
374
+ color: var(--c-bug);
375
+ }
376
+ .badge.chore {
377
+ background: rgba(107, 144, 160, 0.12);
378
+ color: var(--c-chore);
379
+ }
380
+
381
+ .card-id {
382
+ font-size: 0.62rem;
383
+ color: var(--fg-muted);
384
+ flex: 1;
385
+ }
386
+
387
+ .meta-badge {
388
+ font-size: 0.55rem;
389
+ padding: 1px 5px;
390
+ border-radius: 3px;
391
+ background: rgba(195, 195, 195, 0.1);
392
+ border: 1px solid rgba(195, 195, 195, 0.45);
393
+ color: var(--fg-muted);
394
+ letter-spacing: 0.03em;
395
+ text-transform: uppercase;
396
+ }
397
+ .meta-badge.blocked {
398
+ background: rgba(212, 112, 112, 0.08);
399
+ border-color: rgba(212, 112, 112, 0.3);
400
+ color: var(--c-bug);
401
+ }
402
+ .meta-badge.blocked.link {
403
+ cursor: pointer;
404
+ text-transform: none;
405
+ }
406
+ .meta-badge.blocked.link:hover {
407
+ background: rgba(212, 112, 112, 0.18);
408
+ border-color: var(--c-bug);
409
+ }
410
+ /* Derived reverse side of blocked_by - this story blocks another. Amber, so
411
+ it reads as "you're holding something up" rather than "you're stuck". */
412
+ .meta-badge.blocks {
413
+ background: rgba(176, 128, 48, 0.09);
414
+ border-color: rgba(176, 128, 48, 0.32);
415
+ color: #b08030;
416
+ }
417
+ .meta-badge.blocks.link {
418
+ cursor: pointer;
419
+ text-transform: none;
420
+ }
421
+ .meta-badge.blocks.link:hover {
422
+ background: rgba(176, 128, 48, 0.2);
423
+ border-color: #b08030;
424
+ }
425
+ /* Symmetric relation (linked_to / linked_from union) - accent rose. */
426
+ .meta-badge.linked {
427
+ background: rgba(192, 130, 162, 0.1);
428
+ border-color: rgba(192, 130, 162, 0.32);
429
+ color: var(--accent);
430
+ }
431
+ .meta-badge.linked.link {
432
+ cursor: pointer;
433
+ text-transform: none;
434
+ }
435
+ .meta-badge.linked.link:hover {
436
+ background: rgba(192, 130, 162, 0.2);
437
+ border-color: var(--accent);
438
+ }
439
+
440
+ .btn-edit {
441
+ display: none;
442
+ background: none;
443
+ border: 1px solid rgba(195, 195, 195, 0.55);
444
+ color: var(--fg-muted);
445
+ font-family: var(--font-mono);
446
+ font-size: 0.57rem;
447
+ padding: 1px 6px;
448
+ border-radius: 3px;
449
+ cursor: pointer;
450
+ transition:
451
+ color 0.12s,
452
+ border-color 0.12s;
453
+ }
454
+ .btn-edit:hover {
455
+ color: var(--accent);
456
+ border-color: var(--accent);
457
+ }
458
+ /* :focus-within puts the actions in the tab order once keyboard focus
459
+ reaches the card (its title row is focusable) - hover-only hid them
460
+ from keyboard users entirely. */
461
+ .card.body-expanded .btn-edit,
462
+ .card:focus-within .btn-edit {
463
+ display: inline-block;
464
+ }
465
+ .card-title-row:focus-visible,
466
+ .subtask:focus-visible {
467
+ outline: 2px solid var(--accent);
468
+ outline-offset: 2px;
469
+ border-radius: 3px;
470
+ }
471
+
472
+ /* Title row - click to expand */
473
+ .card-title-row {
474
+ display: flex;
475
+ align-items: flex-start;
476
+ gap: 4px;
477
+ cursor: pointer;
478
+ }
479
+ .card-title {
480
+ font-size: 0.8rem;
481
+ line-height: 1.35;
482
+ color: var(--fg-strong);
483
+ word-break: break-word;
484
+ flex: 1;
485
+ }
486
+ .expand-chevron {
487
+ font-size: 0.58rem;
488
+ color: var(--fg-muted);
489
+ flex-shrink: 0;
490
+ margin-top: 3px;
491
+ transition: transform 0.18s;
492
+ opacity: 0.5;
493
+ line-height: 1.35;
494
+ }
495
+ .card.body-expanded .expand-chevron {
496
+ transform: rotate(180deg);
497
+ opacity: 0.7;
498
+ }
499
+
500
+ /* Card body - max-height animation */
501
+ .card-body {
502
+ max-height: 0;
503
+ overflow: hidden;
504
+ transition:
505
+ max-height 0.22s ease,
506
+ margin-top 0.18s;
507
+ }
508
+ .card.body-expanded .card-body {
509
+ max-height: 440px;
510
+ overflow-y: auto;
511
+ overflow-x: hidden;
512
+ margin-top: 5px;
513
+ }
514
+
515
+ .body-preview {
516
+ font-size: 0.73rem;
517
+ line-height: 1.55;
518
+ color: var(--fg);
519
+ padding-bottom: 2px;
520
+ overflow-wrap: anywhere;
521
+ }
522
+ .body-preview p {
523
+ margin-bottom: 3px;
524
+ }
525
+ .body-preview ul {
526
+ padding-left: 14px;
527
+ margin-bottom: 3px;
528
+ }
529
+ .body-preview li {
530
+ margin-bottom: 1px;
531
+ }
532
+ .body-preview h1,
533
+ .body-preview h2,
534
+ .body-preview h3 {
535
+ font-size: 0.75rem;
536
+ font-weight: 400;
537
+ margin: 4px 0 2px;
538
+ color: var(--fg-strong);
539
+ letter-spacing: -0.5px;
540
+ }
541
+ .body-preview strong {
542
+ font-weight: 400;
543
+ color: var(--fg-strong);
544
+ }
545
+ .body-preview code {
546
+ font-family: var(--font-mono);
547
+ font-size: 0.68rem;
548
+ background: rgba(192, 130, 162, 0.1);
549
+ border: 1px solid rgba(192, 130, 162, 0.2);
550
+ border-radius: 3px;
551
+ padding: 0 3px;
552
+ overflow-wrap: anywhere;
553
+ }
554
+ .body-preview pre:not(.gherkin) {
555
+ background: rgba(192, 130, 162, 0.07);
556
+ color: var(--fg-strong);
557
+ border: 1px solid rgba(192, 130, 162, 0.15);
558
+ padding: 6px 8px;
559
+ border-radius: 5px;
560
+ font-size: 0.68rem;
561
+ overflow-x: auto;
562
+ margin: 3px 0;
563
+ }
564
+ .empty-body {
565
+ color: var(--fg-muted);
566
+ font-size: 0.7rem;
567
+ font-style: italic;
568
+ }
569
+
570
+ /* Subtasks */
571
+ .body-preview li.subtask {
572
+ list-style: none;
573
+ margin-left: -14px;
574
+ }
575
+ .subtask {
576
+ cursor: pointer;
577
+ }
578
+ .subtask .cb {
579
+ color: var(--accent);
580
+ }
581
+ .subtask:hover .cb {
582
+ color: var(--fg-strong);
583
+ }
584
+ .subtask.done {
585
+ color: var(--fg-muted);
586
+ text-decoration: line-through;
587
+ }
588
+ .meta-badge.subtask-progress {
589
+ text-transform: none;
590
+ }
591
+
592
+ /* Gherkin */
593
+ .gherkin {
594
+ font-family: var(--font-mono);
595
+ font-size: 0.72rem;
596
+ line-height: 1.6;
597
+ white-space: pre-wrap;
598
+ word-break: break-word;
599
+ background: none;
600
+ padding: 0;
601
+ margin: 0;
602
+ }
603
+ .gh-keyword {
604
+ color: var(--accent);
605
+ }
606
+ .gh-title {
607
+ color: var(--fg-strong);
608
+ }
609
+ .gh-step {
610
+ color: var(--fg-strong);
611
+ }
612
+ .gh-param {
613
+ color: #b87040;
614
+ }
615
+ .gh-comment {
616
+ color: var(--fg-muted);
617
+ font-style: italic;
618
+ }
619
+ .gh-tag {
620
+ color: var(--accent);
621
+ font-style: italic;
622
+ }
623
+ .gh-docstr {
624
+ color: var(--fg-muted);
625
+ }
626
+ .gh-table {
627
+ color: var(--fg);
628
+ }
629
+
630
+ /* Card edit mode */
631
+ .card-edit {
632
+ padding: 8px 10px;
633
+ display: none;
634
+ }
635
+
636
+ .edit-header {
637
+ display: flex;
638
+ align-items: center;
639
+ gap: 6px;
640
+ margin-bottom: 7px;
641
+ }
642
+
643
+ .title-input {
644
+ width: 100%;
645
+ font-family: var(--font-mono);
646
+ font-size: 0.8rem;
647
+ color: var(--fg-strong);
648
+ background: rgba(192, 130, 162, 0.05);
649
+ border: 1px solid var(--accent);
650
+ border-radius: var(--radius-input);
651
+ padding: 4px 7px;
652
+ outline: none;
653
+ margin-bottom: 6px;
654
+ }
655
+ .title-input:focus {
656
+ background: #fff;
657
+ }
658
+
659
+ .field-row {
660
+ display: flex;
661
+ align-items: center;
662
+ gap: 6px;
663
+ margin-bottom: 6px;
664
+ }
665
+ .field-label {
666
+ font-size: 0.62rem;
667
+ color: var(--fg-muted);
668
+ white-space: nowrap;
669
+ min-width: 64px;
670
+ }
671
+ .field-input {
672
+ font-family: var(--font-mono);
673
+ font-size: 0.72rem;
674
+ border: 1px solid var(--border);
675
+ border-radius: var(--radius-input);
676
+ padding: 3px 7px;
677
+ color: var(--fg-strong);
678
+ background: rgba(192, 130, 162, 0.03);
679
+ outline: none;
680
+ }
681
+ .field-input:focus {
682
+ border-color: var(--accent);
683
+ background: #fff;
684
+ }
685
+ .field-input.wide {
686
+ flex: 1;
687
+ }
688
+ .field-input.narrow {
689
+ width: 72px;
690
+ text-transform: uppercase;
691
+ }
692
+
693
+ .size-picker {
694
+ display: flex;
695
+ gap: 4px;
696
+ }
697
+ .size-btn {
698
+ font-family: var(--font-mono);
699
+ font-size: 0.62rem;
700
+ padding: 2px 7px;
701
+ border-radius: 3px;
702
+ border: 1px solid var(--border);
703
+ background: none;
704
+ color: var(--fg);
705
+ cursor: pointer;
706
+ transition: all 0.12s;
707
+ }
708
+ .size-btn:hover {
709
+ border-color: var(--accent);
710
+ color: var(--accent);
711
+ }
712
+ .size-btn.active {
713
+ background: var(--accent);
714
+ color: #fff;
715
+ border-color: var(--accent);
716
+ }
717
+
718
+ .body-editor {
719
+ width: 100%;
720
+ min-height: 90px;
721
+ font-family: var(--font-mono);
722
+ font-size: 0.73rem;
723
+ color: var(--fg-strong);
724
+ background: rgba(192, 130, 162, 0.03);
725
+ border: 1px solid var(--border);
726
+ border-radius: var(--radius-input);
727
+ padding: 6px 8px;
728
+ resize: vertical;
729
+ outline: none;
730
+ line-height: 1.55;
731
+ }
732
+ .body-editor:focus {
733
+ border-color: var(--accent);
734
+ background: #fff;
735
+ }
736
+
737
+ .card-actions {
738
+ display: flex;
739
+ align-items: center;
740
+ gap: 5px;
741
+ margin-top: 7px;
742
+ }
743
+ .flex-spacer {
744
+ flex: 1;
745
+ }
746
+
747
+ .btn-primary,
748
+ .btn-secondary,
749
+ .btn-danger {
750
+ font-family: var(--font-mono);
751
+ font-size: 0.63rem;
752
+ padding: 3px 10px;
753
+ border-radius: 4px;
754
+ cursor: pointer;
755
+ transition: all 0.12s;
756
+ }
757
+ .btn-primary {
758
+ background: var(--accent);
759
+ color: #fff;
760
+ border: 1px solid var(--accent);
761
+ }
762
+ .btn-primary:hover {
763
+ opacity: 0.85;
764
+ }
765
+ .btn-secondary {
766
+ background: none;
767
+ color: var(--fg);
768
+ border: 1px solid var(--border);
769
+ }
770
+ .btn-secondary:hover {
771
+ border-color: var(--fg);
772
+ }
773
+ .btn-danger {
774
+ background: none;
775
+ color: var(--c-bug);
776
+ border: 1px solid var(--c-bug);
777
+ }
778
+ .btn-danger:hover {
779
+ background: rgba(212, 112, 112, 0.08);
780
+ }
781
+
782
+ /* Drop indicator */
783
+ .drop-line {
784
+ height: 2px;
785
+ background: var(--accent);
786
+ border-radius: 2px;
787
+ flex-shrink: 0;
788
+ }
789
+
790
+ /* Reveal story btn */
791
+ .btn-reveal-story {
792
+ display: none;
793
+ background: none;
794
+ border: 1px solid rgba(195, 195, 195, 0.5);
795
+ color: var(--fg-muted);
796
+ font-family: var(--font-mono);
797
+ font-size: 0.57rem;
798
+ padding: 1px 5px;
799
+ border-radius: 3px;
800
+ cursor: pointer;
801
+ transition: all 0.12s;
802
+ }
803
+ .card:hover .btn-reveal-story,
804
+ .card:focus-within .btn-reveal-story {
805
+ display: inline-block;
806
+ }
807
+ .btn-reveal-story:hover {
808
+ color: var(--accent);
809
+ border-color: var(--accent);
810
+ }
811
+
812
+ .btn-expand {
813
+ display: none;
814
+ background: none;
815
+ border: 1px solid rgba(195, 195, 195, 0.5);
816
+ color: var(--fg-muted);
817
+ font-family: var(--font-mono);
818
+ font-size: 0.62rem;
819
+ padding: 1px 5px;
820
+ border-radius: 3px;
821
+ cursor: pointer;
822
+ transition: all 0.12s;
823
+ }
824
+ .card:hover .btn-expand,
825
+ .card:focus-within .btn-expand,
826
+ .card.body-expanded .btn-expand {
827
+ display: inline-block;
828
+ }
829
+ .btn-expand:hover {
830
+ color: var(--accent);
831
+ border-color: var(--accent);
832
+ }
833
+
834
+ /* Story detail modal (BT-070) */
835
+ <%= erb :'_detail_css', layout: false %>
836
+
837
+ /* ── Mobile tab bar ──────────────────────────────────────────── */
838
+
839
+ .mobile-tabs {
840
+ display: none;
841
+ }
842
+
843
+ @media (max-width: 640px) {
844
+ .board {
845
+ padding: 0;
846
+ gap: 0;
847
+ }
848
+ .column {
849
+ display: none;
850
+ flex: 1;
851
+ border-radius: 0 !important;
852
+ border: none !important;
853
+ background: var(--bg) !important;
854
+ }
855
+ .column.mobile-active {
856
+ display: flex;
857
+ }
858
+ .column.collapsed {
859
+ width: auto;
860
+ }
861
+ .column.collapsed .cards,
862
+ .column.collapsed .pagination {
863
+ display: flex;
864
+ }
865
+ .btn-collapse {
866
+ display: none;
867
+ }
868
+ .btn-reveal-story {
869
+ display: none !important;
870
+ }
871
+ .reveal-link {
872
+ display: none;
873
+ }
874
+ .cards {
875
+ padding: 0 10px 10px;
876
+ }
877
+ .btn-add {
878
+ width: 28px;
879
+ height: 28px;
880
+ }
881
+ .col-header {
882
+ padding: 12px 14px 8px;
883
+ }
884
+ .card {
885
+ cursor: default;
886
+ }
887
+
888
+ .mobile-tabs {
889
+ display: flex;
890
+ background: var(--fg-strong);
891
+ flex-shrink: 0;
892
+ border-top: 1px solid rgba(255, 255, 255, 0.06);
893
+ }
894
+ .mobile-tab {
895
+ flex: 1;
896
+ display: flex;
897
+ flex-direction: column;
898
+ align-items: center;
899
+ justify-content: center;
900
+ padding: 8px 4px 10px;
901
+ font-size: 0.48rem;
902
+ letter-spacing: 0.1em;
903
+ text-transform: uppercase;
904
+ color: rgba(255, 227, 240, 0.28);
905
+ cursor: pointer;
906
+ border: none;
907
+ background: none;
908
+ font-family: var(--font-mono);
909
+ gap: 3px;
910
+ transition: color 0.15s;
911
+ position: relative;
912
+ }
913
+ .mobile-tab.active {
914
+ color: var(--color-pink);
915
+ }
916
+ .mobile-tab.active::after {
917
+ content: "";
918
+ position: absolute;
919
+ top: 0;
920
+ left: 25%;
921
+ right: 25%;
922
+ height: 2px;
923
+ background: var(--accent);
924
+ border-radius: 0 0 2px 2px;
925
+ }
926
+ .tab-count {
927
+ font-size: 0.7rem;
928
+ line-height: 1;
929
+ }
930
+ }
931
+
932
+ /* ── Dark mode (palette lives in _root_css) ─────────────────── */
933
+
934
+ [data-theme="dark"] .create-form {
935
+ background: rgba(14, 8, 12, 0.75);
936
+ }
937
+ [data-theme="dark"] .create-title {
938
+ background: rgba(255, 255, 255, 0.05);
939
+ color: var(--fg-strong);
940
+ }
941
+ [data-theme="dark"] .create-title:focus {
942
+ background: rgba(255, 255, 255, 0.09);
943
+ }
944
+ [data-theme="dark"] .title-input {
945
+ background: rgba(255, 255, 255, 0.04);
946
+ }
947
+ [data-theme="dark"] .title-input:focus {
948
+ background: rgba(255, 255, 255, 0.07);
949
+ }
950
+ [data-theme="dark"] .field-input {
951
+ background: rgba(255, 255, 255, 0.04);
952
+ }
953
+ [data-theme="dark"] .field-input:focus {
954
+ background: rgba(255, 255, 255, 0.07);
955
+ }
956
+ [data-theme="dark"] .body-editor {
957
+ background: rgba(255, 255, 255, 0.04);
958
+ }
959
+ [data-theme="dark"] .body-editor:focus {
960
+ background: rgba(255, 255, 255, 0.07);
961
+ }
962
+ [data-theme="dark"] .next-bar {
963
+ background: rgba(192, 130, 162, 0.13);
964
+ border-bottom-color: rgba(192, 130, 162, 0.25);
965
+ }
966
+ [data-theme="dark"] .mobile-tabs {
967
+ background: #0e0a0d;
968
+ border-top-color: rgba(255, 255, 255, 0.04);
969
+ }
970
+ [data-theme="dark"] .size-btn {
971
+ color: var(--fg);
972
+ }
973
+ [data-theme="dark"] .btn-secondary {
974
+ color: var(--fg);
975
+ }
976
+ [data-theme="dark"] .count {
977
+ background: rgba(255, 255, 255, 0.07);
978
+ }
979
+ [data-theme="dark"] .body-preview pre:not(.gherkin) {
980
+ background: #111820;
981
+ color: #c8d4e0;
982
+ border-color: rgba(255, 255, 255, 0.08);
983
+ }
984
+ </style>
985
+ </head>
986
+ <body>
987
+ <header>
988
+ <% if @show_back %>
989
+ <a href="/" class="back-link">← dashboard</a>
990
+ <% end %>
991
+ <span class="carrot-mark" aria-hidden="true">🥕</span>
992
+ <h1><%= h(@project_name || "bacon tracker") %></h1>
993
+ <span class="ns"><%= h @namespace %></span>
994
+ <div class="header-spacer"></div>
995
+ <button class="theme-toggle" title="dark mode">◐</button>
996
+ <button id="btn-reveal-backlog" class="reveal-link" style="display:none" title="Reveal the tracker directory">↗</button>
997
+ </header>
998
+ <div id="next-bar" class="next-bar" style="display:none">
999
+ <span class="next-label">next:</span>
1000
+ <span class="next-title" id="next-title-text"></span>
1001
+ </div>
1002
+ <div id="load-error" class="load-error" style="display:none">
1003
+ <span>couldn't reach the tracker</span>
1004
+ <button id="load-retry">retry</button>
1005
+ </div>
1006
+ <main class="board">
1007
+ <div class="column" data-stage="1_icebox">
1008
+ <div class="col-header">
1009
+ <div class="col-label">
1010
+ <h2>Icebox</h2>
1011
+ <span class="col-sublabel">maybes</span>
1012
+ </div>
1013
+ <div class="col-right">
1014
+ <span class="count">0</span>
1015
+ <button class="btn-add" title="Add to icebox">+</button>
1016
+ </div>
1017
+ </div>
1018
+ <div class="create-form"></div>
1019
+ <div class="cards"></div>
1020
+ </div>
1021
+ <div class="column" data-stage="2_backlog">
1022
+ <div class="col-header">
1023
+ <div class="col-label">
1024
+ <h2>Backlog</h2>
1025
+ <span class="col-sublabel">committed</span>
1026
+ </div>
1027
+ <div class="col-right">
1028
+ <span class="count">0</span>
1029
+ <button class="btn-add" title="Add to backlog">+</button>
1030
+ </div>
1031
+ </div>
1032
+ <div class="create-form"></div>
1033
+ <div class="cards"></div>
1034
+ </div>
1035
+ <div class="column" data-stage="3_started">
1036
+ <div class="col-header">
1037
+ <h2>Started</h2>
1038
+ <div class="col-right"><span class="count">0</span></div>
1039
+ </div>
1040
+ <div class="cards"></div>
1041
+ </div>
1042
+ <div class="column" data-stage="4_done">
1043
+ <div class="col-header">
1044
+ <h2>Done</h2>
1045
+ <div class="col-right">
1046
+ <span class="count">0</span>
1047
+ <button class="btn-collapse" title="Collapse done">−</button>
1048
+ </div>
1049
+ </div>
1050
+ <div class="cards"></div>
1051
+ <div class="pagination"></div>
1052
+ </div>
1053
+ </main>
1054
+ <nav class="mobile-tabs">
1055
+ <button class="mobile-tab" data-target="1_icebox"><span class="tab-count" id="tc-icebox">0</span>icebox</button>
1056
+ <button class="mobile-tab" data-target="2_backlog"><span class="tab-count" id="tc-backlog">0</span>backlog</button>
1057
+ <button class="mobile-tab" data-target="3_started"><span class="tab-count" id="tc-started">0</span>started</button>
1058
+ <button class="mobile-tab" data-target="4_done"><span class="tab-count" id="tc-done">0</span>done</button>
1059
+ </nav>
1060
+ <script>window.BT_API_BASE = <%= (@api_base || '').to_json %>;</script>
1061
+ <script src="/theme.js"></script>
1062
+ <script src="/logic.js"></script>
1063
+ <script src="/app.js"></script>
1064
+ </body>
1065
+ </html>