diff --git a/internal/web/handlers_test.go b/internal/web/handlers_test.go
index 1c656cb..9d60956 100644
--- a/internal/web/handlers_test.go
+++ b/internal/web/handlers_test.go
@@ -234,6 +234,9 @@ func TestDetailRendersHighlightsAndTakeaways(t *testing.T) {
require.Contains(t, html, "takeaway one")
require.Contains(t, html, `id="action-buttons"`, "action button group present")
require.Contains(t, html, "Watched")
+ require.Contains(t, html, `class="segmented"`, "watched/skipped render as a segmented control (UX review C5)")
+ require.Less(t, strings.Index(html, "Skipped"), strings.Index(html, "Saved"),
+ "Saved sits after the watched/skipped segment")
require.Contains(t, html, "← Summaries", "back link to the list (UX review C3)")
// The detail page leads with the attention-saving payload (UX review A8):
diff --git a/internal/web/view.go b/internal/web/view.go
index f435ba8..5125e3b 100644
--- a/internal/web/view.go
+++ b/internal/web/view.go
@@ -684,8 +684,13 @@ a.btn, a.btn:visited { color: var(--accent-fg); }
.detail ul { margin: 0; padding-left: 1.2rem; line-height: 1.6; }
.detail li { margin-bottom: var(--s1); }
-/* action toggles */
-.actions { display: flex; gap: var(--s2); margin: var(--s4) 0; flex-wrap: wrap; }
+/* action toggles — watched|skipped form one segmented control (they are mutually
+ exclusive), "saved" sits apart as an independent toggle */
+.actions { display: flex; gap: var(--s3); margin: var(--s4) 0; flex-wrap: wrap; align-items: center; }
+.segmented { display: inline-flex; }
+.segmented .action { border-radius: 0; border-right-width: 0; }
+.segmented .action:first-child { border-top-left-radius: var(--radius); border-bottom-left-radius: var(--radius); }
+.segmented .action:last-child { border-top-right-radius: var(--radius); border-bottom-right-radius: var(--radius); border-right-width: 1px; }
.actions .action { font: inherit; padding: .4rem .9rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--fg); cursor: pointer; transition: border-color .15s, background .15s; }
.actions .action:hover { border-color: var(--accent); }
.actions .action:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
diff --git a/internal/web/views.templ b/internal/web/views.templ
index 9ab4991..ed34a72 100644
--- a/internal/web/views.templ
+++ b/internal/web/views.templ
@@ -530,20 +530,31 @@ templ ActionButtons(videoID string, active map[string]bool) {
hx-target="#action-buttons"
hx-swap="outerHTML"
>
- for _, v := range actionVerbs {
-
- }
+ // watched ↔ skipped are mutually exclusive (the store clears one when the
+ // other is set), so they read as a single segmented choice. "saved" is an
+ // independent toggle and sits apart (UX review C5).
+
+ @actionButton("watched", active["watched"])
+ @actionButton("skipped", active["skipped"])
+
+ @actionButton("saved", active["saved"])
}
+
+// actionButton is one toggle button in the action group: a submit carrying its
+// verb, marked active (accent fill + ✓ prefix + aria-pressed) when currently set.
+templ actionButton(verb string, isActive bool) {
+
+}
diff --git a/internal/web/views_templ.go b/internal/web/views_templ.go
index 7775c59..9dca6bb 100644
--- a/internal/web/views_templ.go
+++ b/internal/web/views_templ.go
@@ -1862,86 +1862,127 @@ func ActionButtons(videoID string, active map[string]bool) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 153, "\" hx-target=\"#action-buttons\" hx-swap=\"outerHTML\">")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 153, "\" hx-target=\"#action-buttons\" hx-swap=\"outerHTML\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, v := range actionVerbs {
- var templ_7745c5c3_Var81 = []any{"action", templ.KV("active", active[v])}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var81...)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 154, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = actionButton("saved", active["saved"]).Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 155, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+}
+
+// actionButton is one toggle button in the action group: a submit carrying its
+// verb, marked active (accent fill + ✓ prefix + aria-pressed) when currently set.
+func actionButton(verb string, isActive bool) templ.Component {
+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
+ return templ_7745c5c3_CtxErr
+ }
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var81 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var81 == nil {
+ templ_7745c5c3_Var81 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ var templ_7745c5c3_Var82 = []any{"action", templ.KV("active", isActive)}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var82...)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 156, "")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var86))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ var templ_7745c5c3_Var87 string
+ templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.JoinStringErrs(actionLabel(verb))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 557, Col: 22}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var87))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 159, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 160, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}