fix(styles): use semantic names in styles + drop deadcode

Christian Rocha created

Change summary

internal/app/app.go                 |   6 
internal/cmd/run.go                 |   6 
internal/ui/chat/assistant.go       |  28 
internal/ui/chat/docker_mcp.go      |  10 
internal/ui/chat/messages.go        |  10 
internal/ui/chat/todos.go           |  12 
internal/ui/chat/tools.go           |  14 
internal/ui/chat/user.go            |   4 
internal/ui/common/button.go        |   4 
internal/ui/common/elements.go      |  28 
internal/ui/common/markdown.go      |   6 
internal/ui/dialog/api_key_input.go |   4 
internal/ui/dialog/arguments.go     |   2 
internal/ui/dialog/commands.go      |   4 
internal/ui/dialog/commands_item.go |   4 
internal/ui/dialog/common.go        |  10 
internal/ui/dialog/models.go        |  10 
internal/ui/dialog/models_item.go   |   6 
internal/ui/dialog/oauth.go         |  23 
internal/ui/dialog/permissions.go   |   8 
internal/ui/dialog/quit.go          |   4 
internal/ui/dialog/reasoning.go     |   4 
internal/ui/dialog/sessions_item.go |   4 
internal/ui/logo/example/main.go    |  14 
internal/ui/logo/logo.go            |   6 
internal/ui/model/header.go         |   4 
internal/ui/model/layout_test.go    |   2 
internal/ui/model/lsp.go            |  32 
internal/ui/model/mcp.go            |  32 
internal/ui/model/pills.go          |  12 
internal/ui/model/session.go        |   6 
internal/ui/model/sidebar.go        |   2 
internal/ui/model/skills.go         |  16 
internal/ui/model/skills_test.go    |   4 
internal/ui/model/ui.go             |  28 
internal/ui/styles/styles.go        | 682 +++++++++++++++++-------------
36 files changed, 571 insertions(+), 480 deletions(-)

Detailed changes

internal/app/app.go 🔗

@@ -242,14 +242,14 @@ func (app *App) RunNonInteractive(ctx context.Context, output io.Writer, prompt,
 		if f, ok := output.(*os.File); ok && stdinTTY && stdoutTTY {
 			hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, f)
 		}
-		defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
+		defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
 
 		spinner = format.NewSpinner(ctx, cancel, anim.Settings{
 			Size:        10,
 			Label:       "Generating",
 			LabelColor:  defaultFG,
-			GradColorA:  t.Primary,
-			GradColorB:  t.Secondary,
+			GradColorA:  t.WorkingGradFromColor,
+			GradColorB:  t.WorkingGradToColor,
 			CycleColors: true,
 		})
 		spinner.Start()

internal/cmd/run.go 🔗

@@ -195,14 +195,14 @@ func runNonInteractive(
 		if stdinTTY && stdoutTTY {
 			hasDarkBG = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
 		}
-		defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.FgBase)
+		defaultFG := lipgloss.LightDark(hasDarkBG)(charmtone.Pepper, t.WorkingLabelColor)
 
 		spinner = format.NewSpinner(ctx, cancel, anim.Settings{
 			Size:        10,
 			Label:       "Generating",
 			LabelColor:  defaultFG,
-			GradColorA:  t.Primary,
-			GradColorB:  t.Secondary,
+			GradColorA:  t.WorkingGradFromColor,
+			GradColorB:  t.WorkingGradToColor,
 			CycleColors: true,
 		})
 		spinner.Start()

internal/ui/chat/assistant.go 🔗

@@ -48,9 +48,9 @@ func NewAssistantMessageItem(sty *styles.Styles, message *message.Message) Messa
 	a.anim = anim.New(anim.Settings{
 		ID:          a.ID(),
 		Size:        15,
-		GradColorA:  sty.Primary,
-		GradColorB:  sty.Secondary,
-		LabelColor:  sty.FgBase,
+		GradColorA:  sty.WorkingGradFromColor,
+		GradColorB:  sty.WorkingGradToColor,
+		LabelColor:  sty.WorkingLabelColor,
 		CycleColors: true,
 	})
 	return a
@@ -112,8 +112,8 @@ func (a *AssistantMessageItem) Render(width int) string {
 	// it's wrapping logic.
 	// We already know that the content is wrapped to the correct width in
 	// RawRender, so we can just apply the styles directly to each line.
-	focused := a.sty.Chat.Message.AssistantFocused.Render()
-	blurred := a.sty.Chat.Message.AssistantBlurred.Render()
+	focused := a.sty.Messages.AssistantFocused.Render()
+	blurred := a.sty.Messages.AssistantBlurred.Render()
 	rendered := a.RawRender(width)
 	lines := strings.Split(rendered, "\n")
 	for i, line := range lines {
@@ -149,7 +149,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
 	if a.message.IsFinished() {
 		switch a.message.FinishReason() {
 		case message.FinishReasonCanceled:
-			messageParts = append(messageParts, a.sty.Base.Italic(true).Render("Canceled"))
+			messageParts = append(messageParts, a.sty.Messages.AssistantCanceled.Render("Canceled"))
 		case message.FinishReasonError:
 			messageParts = append(messageParts, a.renderError(width))
 		}
@@ -160,7 +160,7 @@ func (a *AssistantMessageItem) renderMessageContent(width int) string {
 
 // renderThinking renders the thinking/reasoning content with footer.
 func (a *AssistantMessageItem) renderThinking(thinking string, width int) string {
-	renderer := common.PlainMarkdownRenderer(a.sty, width)
+	renderer := common.QuietMarkdownRenderer(a.sty, width)
 	rendered, err := renderer.Render(thinking)
 	if err != nil {
 		rendered = thinking
@@ -173,13 +173,13 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
 	isTruncated := totalLines > maxCollapsedThinkingHeight
 	if !a.thinkingExpanded && isTruncated {
 		lines = lines[totalLines-maxCollapsedThinkingHeight:]
-		hint := a.sty.Chat.Message.ThinkingTruncationHint.Render(
+		hint := a.sty.Messages.ThinkingTruncationHint.Render(
 			fmt.Sprintf(assistantMessageTruncateFormat, totalLines-maxCollapsedThinkingHeight),
 		)
 		lines = append([]string{hint, ""}, lines...)
 	}
 
-	thinkingStyle := a.sty.Chat.Message.ThinkingBox.Width(width)
+	thinkingStyle := a.sty.Messages.ThinkingBox.Width(width)
 	result := thinkingStyle.Render(strings.Join(lines, "\n"))
 	a.thinkingBoxHeight = lipgloss.Height(result)
 
@@ -188,8 +188,8 @@ func (a *AssistantMessageItem) renderThinking(thinking string, width int) string
 	if !a.message.IsThinking() || len(a.message.ToolCalls()) > 0 {
 		duration := a.message.ThinkingDuration()
 		if duration.String() != "0s" {
-			footer = a.sty.Chat.Message.ThinkingFooterTitle.Render("Thought for ") +
-				a.sty.Chat.Message.ThinkingFooterDuration.Render(duration.String())
+			footer = a.sty.Messages.ThinkingFooterTitle.Render("Thought for ") +
+				a.sty.Messages.ThinkingFooterDuration.Render(duration.String())
 		}
 	}
 
@@ -222,10 +222,10 @@ func (a *AssistantMessageItem) renderSpinning() string {
 // renderError renders an error message.
 func (a *AssistantMessageItem) renderError(width int) string {
 	finishPart := a.message.FinishPart()
-	errTag := a.sty.Chat.Message.ErrorTag.Render("ERROR")
+	errTag := a.sty.Messages.ErrorTag.Render("ERROR")
 	truncated := ansi.Truncate(finishPart.Message, width-2-lipgloss.Width(errTag), "...")
-	title := fmt.Sprintf("%s %s", errTag, a.sty.Chat.Message.ErrorTitle.Render(truncated))
-	details := a.sty.Chat.Message.ErrorDetails.Width(width - 2).Render(finishPart.Details)
+	title := fmt.Sprintf("%s %s", errTag, a.sty.Messages.ErrorTitle.Render(truncated))
+	details := a.sty.Messages.ErrorDetails.Width(width - 2).Render(finishPart.Details)
 	return fmt.Sprintf("%s\n\n%s", title, details)
 }
 

internal/ui/chat/docker_mcp.go 🔗

@@ -173,7 +173,7 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
 	}
 
 	if len(result.Servers) == 0 {
-		return sty.Subtle.Render("No MCP servers found.")
+		return sty.Tool.ResultEmpty.Render("No MCP servers found.")
 	}
 
 	bodyWidth := min(120, width) - toolBodyLeftPaddingTotal
@@ -181,10 +181,10 @@ func (d *DockerMCPToolRenderContext) renderMCPServers(sty *styles.Styles, opts *
 	moreServers := ""
 	for i, server := range result.Servers {
 		if i > 9 {
-			moreServers = sty.Subtle.Render(fmt.Sprintf("... and %d more", len(result.Servers)-10))
+			moreServers = sty.Tool.ResultTruncation.Render(fmt.Sprintf("... and %d more", len(result.Servers)-10))
 			break
 		}
-		rows = append(rows, []string{sty.Base.Render(server.Name), sty.Subtle.Render(server.Description)})
+		rows = append(rows, []string{sty.Tool.ResultItemName.Render(server.Name), sty.Tool.ResultItemDesc.Render(server.Description)})
 	}
 	serverTable := table.New().
 		Wrap(false).
@@ -236,10 +236,10 @@ func (d *DockerMCPToolRenderContext) formatToolName(sty *styles.Styles, tool str
 		action = "Find"
 	case "mcp-add":
 		action = "Add"
-		actionStyle = sty.Tool.DockerMCPActionAdd
+		actionStyle = sty.Tool.ActionCreate
 	case "mcp-remove":
 		action = "Remove"
-		actionStyle = sty.Tool.DockerMCPActionDel
+		actionStyle = sty.Tool.ActionDestroy
 	case "code-mode":
 		action = "Code Mode"
 	default:

internal/ui/chat/messages.go 🔗

@@ -221,7 +221,7 @@ func (a *AssistantInfoItem) RawRender(width int) string {
 
 // Render implements MessageItem.
 func (a *AssistantInfoItem) Render(width int) string {
-	prefix := a.sty.Chat.Message.SectionHeader.Render()
+	prefix := a.sty.Messages.SectionHeader.Render()
 	lines := strings.Split(a.RawRender(width), "\n")
 	for i, line := range lines {
 		lines[i] = prefix + line
@@ -236,18 +236,18 @@ func (a *AssistantInfoItem) renderContent(width int) string {
 	}
 	finishTime := time.Unix(finishData.Time, 0)
 	duration := finishTime.Sub(a.lastUserMessageTime)
-	infoMsg := a.sty.Chat.Message.AssistantInfoDuration.Render(duration.String())
-	icon := a.sty.Chat.Message.AssistantInfoIcon.Render(styles.ModelIcon)
+	infoMsg := a.sty.Messages.AssistantInfoDuration.Render(duration.String())
+	icon := a.sty.Messages.AssistantInfoIcon.Render(styles.ModelIcon)
 	model := a.cfg.GetModel(a.message.Provider, a.message.Model)
 	if model == nil {
 		model = &catwalk.Model{Name: "Unknown Model"}
 	}
-	modelFormatted := a.sty.Chat.Message.AssistantInfoModel.Render(model.Name)
+	modelFormatted := a.sty.Messages.AssistantInfoModel.Render(model.Name)
 	providerName := a.message.Provider
 	if providerConfig, ok := a.cfg.Providers.Get(a.message.Provider); ok {
 		providerName = providerConfig.Name
 	}
-	provider := a.sty.Chat.Message.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName))
+	provider := a.sty.Messages.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName))
 	assistant := fmt.Sprintf("%s %s %s %s", icon, modelFormatted, provider, infoMsg)
 	return common.Section(a.sty, assistant, width)
 }

internal/ui/chat/todos.go 🔗

@@ -91,16 +91,16 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
 
 					ratio := sty.Tool.TodoRatio.Render(fmt.Sprintf("%d/%d", meta.Completed, meta.Total))
 					if hasCompleted && hasStarted {
-						text := sty.Subtle.Render(fmt.Sprintf(" · completed %d, starting next", len(meta.JustCompleted)))
+						text := sty.Tool.TodoStatusNote.Render(fmt.Sprintf(" · completed %d, starting next", len(meta.JustCompleted)))
 						headerText = fmt.Sprintf("%s%s", ratio, text)
 					} else if hasCompleted {
-						text := sty.Subtle.Render(fmt.Sprintf(" · completed %d", len(meta.JustCompleted)))
+						text := sty.Tool.TodoStatusNote.Render(fmt.Sprintf(" · completed %d", len(meta.JustCompleted)))
 						if allCompleted {
-							text = sty.Subtle.Render(" · completed all")
+							text = sty.Tool.TodoStatusNote.Render(" · completed all")
 						}
 						headerText = fmt.Sprintf("%s%s", ratio, text)
 					} else if hasStarted {
-						headerText = fmt.Sprintf("%s%s", ratio, sty.Subtle.Render(" · starting task"))
+						headerText = fmt.Sprintf("%s%s", ratio, sty.Tool.TodoStatusNote.Render(" · starting task"))
 					} else {
 						headerText = ratio
 					}
@@ -111,7 +111,7 @@ func (t *TodosToolRenderContext) RenderTool(sty *styles.Styles, width int, opts
 						body = FormatTodosList(sty, meta.Todos, styles.ArrowRightIcon, cappedWidth)
 					} else if meta.JustStarted != "" {
 						body = sty.Tool.TodoInProgressIcon.Render(styles.ArrowRightIcon+" ") +
-							sty.Base.Render(meta.JustStarted)
+							sty.Tool.TodoJustStarted.Render(meta.JustStarted)
 					}
 				}
 			}
@@ -148,7 +148,7 @@ func FormatTodosList(sty *styles.Styles, todos []session.Todo, inProgressIcon st
 	var lines []string
 	for _, todo := range sorted {
 		var prefix string
-		textStyle := sty.Base
+		textStyle := sty.Tool.TodoItem
 
 		switch todo.Status {
 		case session.TodoStatusCompleted:

internal/ui/chat/tools.go 🔗

@@ -189,9 +189,9 @@ func newBaseToolMessageItem(
 	t.anim = anim.New(anim.Settings{
 		ID:          toolCall.ID,
 		Size:        15,
-		GradColorA:  sty.Primary,
-		GradColorB:  sty.Secondary,
-		LabelColor:  sty.FgBase,
+		GradColorA:  sty.WorkingGradFromColor,
+		GradColorB:  sty.WorkingGradToColor,
+		LabelColor:  sty.WorkingLabelColor,
 		CycleColors: true,
 	})
 
@@ -325,11 +325,11 @@ func (t *baseToolMessageItem) RawRender(width int) string {
 func (t *baseToolMessageItem) Render(width int) string {
 	var prefix string
 	if t.isCompact {
-		prefix = t.sty.Chat.Message.ToolCallCompact.Render()
+		prefix = t.sty.Messages.ToolCallCompact.Render()
 	} else if t.focused {
-		prefix = t.sty.Chat.Message.ToolCallFocused.Render()
+		prefix = t.sty.Messages.ToolCallFocused.Render()
 	} else {
-		prefix = t.sty.Chat.Message.ToolCallBlurred.Render()
+		prefix = t.sty.Messages.ToolCallBlurred.Render()
 	}
 	lines := strings.Split(t.RawRender(width), "\n")
 	for i, ln := range lines {
@@ -799,7 +799,7 @@ func toolOutputMarkdownContent(sty *styles.Styles, content string, width int, ex
 		width = maxTextWidth
 	}
 
-	renderer := common.PlainMarkdownRenderer(sty, width)
+	renderer := common.QuietMarkdownRenderer(sty, width)
 	rendered, err := renderer.Render(content)
 	if err != nil {
 		return toolOutputPlainContent(sty, content, width, expanded)

internal/ui/chat/user.go 🔗

@@ -72,9 +72,9 @@ func (m *UserMessageItem) RawRender(width int) string {
 func (m *UserMessageItem) Render(width int) string {
 	var prefix string
 	if m.focused {
-		prefix = m.sty.Chat.Message.UserFocused.Render()
+		prefix = m.sty.Messages.UserFocused.Render()
 	} else {
-		prefix = m.sty.Chat.Message.UserBlurred.Render()
+		prefix = m.sty.Messages.UserBlurred.Render()
 	}
 	lines := strings.Split(m.RawRender(width), "\n")
 	for i, line := range lines {

internal/ui/common/button.go 🔗

@@ -22,9 +22,9 @@ type ButtonOpts struct {
 // Button creates a button with an underlined character and selection state
 func Button(t *styles.Styles, opts ButtonOpts) string {
 	// Select style based on selection state
-	style := t.ButtonBlur
+	style := t.Button.Blurred
 	if opts.Selected {
-		style = t.ButtonFocus
+		style = t.Button.Focused
 	}
 
 	text := opts.Text

internal/ui/common/elements.go 🔗

@@ -18,7 +18,7 @@ import (
 // muted styling.
 func PrettyPath(t *styles.Styles, path string, width int) string {
 	formatted := home.Short(path)
-	return t.Muted.Width(width).Render(formatted)
+	return t.Sidebar.WorkingDir.Width(width).Render(formatted)
 }
 
 // FormatReasoningEffort formats a reasoning effort level for display.
@@ -39,13 +39,13 @@ type ModelContextInfo struct {
 // ModelInfo renders model information including name, provider, reasoning
 // settings, and optional context usage/cost.
 func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string, context *ModelContextInfo, width int) string {
-	modelIcon := t.Subtle.Render(styles.ModelIcon)
-	modelName = t.Base.Render(modelName)
+	modelIcon := t.ModelInfo.Icon.Render(styles.ModelIcon)
+	modelName = t.ModelInfo.Name.Render(modelName)
 
 	// Build first line with model name and optionally provider on the same line
 	var firstLine string
 	if providerName != "" {
-		providerInfo := t.Muted.Render(fmt.Sprintf("via %s", providerName))
+		providerInfo := t.ModelInfo.Provider.Render(fmt.Sprintf("via %s", providerName))
 		modelWithProvider := fmt.Sprintf("%s %s %s", modelIcon, modelName, providerInfo)
 
 		// Check if it fits on one line
@@ -64,11 +64,11 @@ func ModelInfo(t *styles.Styles, modelName, providerName, reasoningInfo string,
 	// If provider didn't fit on first line, add it as second line
 	if providerName != "" && !strings.Contains(firstLine, "via") {
 		providerInfo := fmt.Sprintf("via %s", providerName)
-		parts = append(parts, t.Muted.PaddingLeft(2).Render(providerInfo))
+		parts = append(parts, t.ModelInfo.ProviderFallback.Render(providerInfo))
 	}
 
 	if reasoningInfo != "" {
-		parts = append(parts, t.Subtle.PaddingLeft(2).Render(reasoningInfo))
+		parts = append(parts, t.ModelInfo.Reasoning.Render(reasoningInfo))
 	}
 
 	if context != nil {
@@ -103,10 +103,10 @@ func formatTokensAndCost(t *styles.Styles, tokens, contextWindow int64, cost flo
 
 	percentage := (float64(tokens) / float64(contextWindow)) * 100
 
-	formattedCost := t.Muted.Render(fmt.Sprintf("$%.2f", cost))
+	formattedCost := t.ModelInfo.Cost.Render(fmt.Sprintf("$%.2f", cost))
 
-	formattedTokens = t.Subtle.Render(fmt.Sprintf("(%s)", formattedTokens))
-	formattedPercentage := t.Muted.Render(fmt.Sprintf("%d%%", int(percentage)))
+	formattedTokens = t.ModelInfo.TokenCount.Render(fmt.Sprintf("(%s)", formattedTokens))
+	formattedPercentage := t.ModelInfo.TokenPercentage.Render(fmt.Sprintf("%d%%", int(percentage)))
 	formattedTokens = fmt.Sprintf("%s %s", formattedPercentage, formattedTokens)
 	if percentage > 80 {
 		formattedTokens = fmt.Sprintf("%s %s", styles.LSPWarningIcon, formattedTokens)
@@ -133,10 +133,10 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
 	title := opts.Title
 	description := opts.Description
 
-	titleColor := cmp.Or(opts.TitleColor, t.Muted.GetForeground())
-	descriptionColor := cmp.Or(opts.DescriptionColor, t.Subtle.GetForeground())
+	titleColor := cmp.Or(opts.TitleColor, t.Resource.DefaultTitleFg)
+	descriptionColor := cmp.Or(opts.DescriptionColor, t.Resource.DefaultDescFg)
 
-	title = t.Base.Foreground(titleColor).Render(title)
+	title = t.Resource.RowTitleBase.Foreground(titleColor).Render(title)
 
 	if description != "" {
 		extraContentWidth := lipgloss.Width(opts.ExtraContent)
@@ -144,7 +144,7 @@ func Status(t *styles.Styles, opts StatusOpts, width int) string {
 			extraContentWidth += 1
 		}
 		description = ansi.Truncate(description, width-lipgloss.Width(icon)-lipgloss.Width(title)-2-extraContentWidth, "…")
-		description = t.Base.Foreground(descriptionColor).Render(description)
+		description = t.Resource.RowDescBase.Foreground(descriptionColor).Render(description)
 	}
 
 	var content []string
@@ -193,7 +193,7 @@ func DialogTitle(t *styles.Styles, title string, width int, fromColor, toColor c
 	remainingWidth := width - length
 	if remainingWidth > 0 {
 		lines := strings.Repeat(char, remainingWidth)
-		lines = styles.ApplyForegroundGrad(t.Base, lines, fromColor, toColor)
+		lines = styles.ApplyForegroundGrad(t.Dialog.TitleLineBase, lines, fromColor, toColor)
 		title = title + " " + lines
 	}
 	return title

internal/ui/common/markdown.go 🔗

@@ -29,11 +29,11 @@ func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
 	return r
 }
 
-// PlainMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
+// QuietMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
 // (plain text with structure) and the given width.
-func PlainMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
+func QuietMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
 	r, _ := glamour.NewTermRenderer(
-		glamour.WithStyles(sty.PlainMarkdown),
+		glamour.WithStyles(sty.QuietMarkdown),
 		glamour.WithWordWrap(width),
 		glamour.WithChromaFormatter(formatterName),
 	)

internal/ui/dialog/api_key_input.go 🔗

@@ -83,7 +83,7 @@ func NewAPIKeyInput(
 
 	m.spinner = spinner.New(
 		spinner.WithSpinner(spinner.Dot),
-		spinner.WithStyle(t.Base.Foreground(t.Green)),
+		spinner.WithStyle(t.Dialog.APIKey.Spinner),
 	)
 
 	m.help = help.New()
@@ -202,7 +202,7 @@ func (m *APIKeyInput) headerView() string {
 		return textStyle.Render(m.dialogTitle())
 	}
 	headerOffset := titleStyle.GetHorizontalFrameSize() + dialogStyle.GetHorizontalFrameSize()
-	return common.DialogTitle(t, titleStyle.Render(m.dialogTitle()), m.width-headerOffset, m.com.Styles.Primary, m.com.Styles.Secondary)
+	return common.DialogTitle(t, titleStyle.Render(m.dialogTitle()), m.width-headerOffset, m.com.Styles.Dialog.TitleGradFromColor, m.com.Styles.Dialog.TitleGradToColor)
 }
 
 func (m *APIKeyInput) dialogTitle() string {

internal/ui/dialog/arguments.go 🔗

@@ -314,7 +314,7 @@ func (a *Arguments) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
 
 	titleText := cmp.Or(a.title, "Arguments")
 
-	header := common.DialogTitle(s, titleText, width, s.Primary, s.Secondary)
+	header := common.DialogTitle(s, titleText, width, s.Dialog.TitleGradFromColor, s.Dialog.TitleGradToColor)
 
 	// Add description if available.
 	var description string

internal/ui/dialog/commands.go 🔗

@@ -252,9 +252,9 @@ func commandsRadioView(sty *styles.Styles, selected CommandType, hasUserCmds boo
 
 	selectedFn := func(t CommandType) string {
 		if t == selected {
-			return sty.RadioOn.Padding(0, 1).Render() + sty.HalfMuted.Render(t.String())
+			return sty.Radio.On.Padding(0, 1).Render() + sty.Radio.Label.Render(t.String())
 		}
-		return sty.RadioOff.Padding(0, 1).Render() + sty.HalfMuted.Render(t.String())
+		return sty.Radio.Off.Padding(0, 1).Render() + sty.Radio.Label.Render(t.String())
 	}
 
 	parts := []string{

internal/ui/dialog/commands_item.go 🔗

@@ -69,8 +69,8 @@ func (c *CommandItem) Render(width int) string {
 	styles := ListItemStyles{
 		ItemBlurred:     c.t.Dialog.NormalItem,
 		ItemFocused:     c.t.Dialog.SelectedItem,
-		InfoTextBlurred: c.t.Base,
-		InfoTextFocused: c.t.Base,
+		InfoTextBlurred: c.t.Dialog.ListItem.InfoBlurred,
+		InfoTextFocused: c.t.Dialog.ListItem.InfoFocused,
 	}
 	return renderItem(styles, c.title, c.shortcut, c.focused, width, c.cache, &c.m)
 }

internal/ui/dialog/common.go 🔗

@@ -65,9 +65,11 @@ type RenderContext struct {
 	TitleStyle lipgloss.Style
 	// ViewStyle is the style of the dialog title by default it uses Styles.Dialog.View
 	ViewStyle lipgloss.Style
-	// TitleGradientFromColor is the color the title gradient starts by defaults its Style.Primary
+	// TitleGradientFromColor is the color the title gradient starts by default
+	// its Styles.Dialog.TitleGradFromColor
 	TitleGradientFromColor color.Color
-	// TitleGradientToColor is the color the title gradient starts by defaults its Style.Secondary
+	// TitleGradientToColor is the color the title gradient ends by default its
+	// Styles.Dialog.TitleGradToColor
 	TitleGradientToColor color.Color
 	// Width is the total width of the dialog including any margins, borders,
 	// and paddings.
@@ -98,8 +100,8 @@ func NewRenderContext(t *styles.Styles, width int) *RenderContext {
 		Styles:                 t,
 		TitleStyle:             t.Dialog.Title,
 		ViewStyle:              t.Dialog.View,
-		TitleGradientFromColor: t.Primary,
-		TitleGradientToColor:   t.Secondary,
+		TitleGradientFromColor: t.Dialog.TitleGradFromColor,
+		TitleGradientToColor:   t.Dialog.TitleGradToColor,
 		Width:                  width,
 		Parts:                  []string{},
 	}

internal/ui/dialog/models.go 🔗

@@ -236,13 +236,13 @@ func (m *Models) Cursor() *tea.Cursor {
 // modelTypeRadioView returns the radio view for model type selection.
 func (m *Models) modelTypeRadioView() string {
 	t := m.com.Styles
-	textStyle := t.HalfMuted
-	largeRadioStyle := t.RadioOff
-	smallRadioStyle := t.RadioOff
+	textStyle := t.Radio.Label
+	largeRadioStyle := t.Radio.Off
+	smallRadioStyle := t.Radio.Off
 	if m.modelType == ModelTypeLarge {
-		largeRadioStyle = t.RadioOn
+		largeRadioStyle = t.Radio.On
 	} else {
-		smallRadioStyle = t.RadioOn
+		smallRadioStyle = t.Radio.On
 	}
 
 	largeRadio := largeRadioStyle.Padding(0, 1).Render()

internal/ui/dialog/models_item.go 🔗

@@ -38,7 +38,7 @@ func (m *ModelGroup) Render(width int) string {
 	var configured string
 	if m.configured {
 		configuredIcon := m.t.ToolCallSuccess.Render()
-		configuredText := m.t.Subtle.Render("Configured")
+		configuredText := m.t.Dialog.Models.ConfiguredText.Render("Configured")
 		configured = configuredIcon + " " + configuredText
 	}
 
@@ -109,8 +109,8 @@ func (m *ModelItem) Render(width int) string {
 	styles := ListItemStyles{
 		ItemBlurred:     m.t.Dialog.NormalItem,
 		ItemFocused:     m.t.Dialog.SelectedItem,
-		InfoTextBlurred: m.t.Base,
-		InfoTextFocused: m.t.Base,
+		InfoTextBlurred: m.t.Dialog.ListItem.InfoBlurred,
+		InfoTextFocused: m.t.Dialog.ListItem.InfoFocused,
 	}
 	return renderItem(styles, m.model.Name, providerInfo, m.focused, width, m.cache, &m.m)
 }

internal/ui/dialog/oauth.go 🔗

@@ -94,7 +94,7 @@ func newOAuth(
 
 	m.spinner = spinner.New(
 		spinner.WithSpinner(spinner.Dot),
-		spinner.WithStyle(t.Base.Foreground(t.GreenLight)),
+		spinner.WithStyle(t.Dialog.OAuth.Spinner),
 	)
 
 	m.help = help.New()
@@ -227,18 +227,18 @@ func (m *OAuth) headerContent() string {
 	if m.isOnboarding {
 		return textStyle.Render(dialogTitle)
 	}
-	return common.DialogTitle(t, titleStyle.Render(dialogTitle), m.width-headerOffset, t.Primary, t.Secondary)
+	return common.DialogTitle(t, titleStyle.Render(dialogTitle), m.width-headerOffset, t.Dialog.TitleGradFromColor, t.Dialog.TitleGradToColor)
 }
 
 func (m *OAuth) innerDialogContent() string {
 	var (
 		t            = m.com.Styles
-		whiteStyle   = lipgloss.NewStyle().Foreground(t.White)
-		primaryStyle = lipgloss.NewStyle().Foreground(t.Primary)
-		greenStyle   = lipgloss.NewStyle().Foreground(t.GreenLight)
-		linkStyle    = lipgloss.NewStyle().Foreground(t.GreenDark).Underline(true)
-		errorStyle   = lipgloss.NewStyle().Foreground(t.Error)
-		mutedStyle   = lipgloss.NewStyle().Foreground(t.FgMuted)
+		whiteStyle   = t.Dialog.OAuth.Instructions
+		primaryStyle = t.Dialog.OAuth.Enter
+		greenStyle   = t.Dialog.OAuth.Success
+		linkStyle    = t.Dialog.OAuth.Link
+		errorStyle   = t.Dialog.OAuth.ErrorText
+		mutedStyle   = t.Dialog.OAuth.StatusText
 	)
 
 	switch m.State {
@@ -266,13 +266,10 @@ func (m *OAuth) innerDialogContent() string {
 			Width(m.width-2).
 			Height(7).
 			Align(lipgloss.Center, lipgloss.Center).
-			Background(t.BgBaseLighter).
+			Background(t.Dialog.OAuth.UserCodeBg).
 			Margin(0, 1).
 			Render(
-				lipgloss.NewStyle().
-					Bold(true).
-					Foreground(t.White).
-					Render(m.userCode),
+				t.Dialog.OAuth.UserCode.Render(m.userCode),
 			)
 
 		link := linkStyle.Hyperlink(m.verificationURL, "id=oauth-verify").Render(m.verificationURL)

internal/ui/dialog/permissions.go 🔗

@@ -443,7 +443,7 @@ func (p *Permissions) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
 func (p *Permissions) renderHeader(contentWidth int) string {
 	t := p.com.Styles
 
-	title := common.DialogTitle(t, "Permission Required", contentWidth-t.Dialog.Title.GetHorizontalFrameSize(), t.Primary, t.Secondary)
+	title := common.DialogTitle(t, "Permission Required", contentWidth-t.Dialog.Title.GetHorizontalFrameSize(), t.Dialog.TitleGradFromColor, t.Dialog.TitleGradToColor)
 	title = t.Dialog.Title.Render(title)
 
 	// Tool info.
@@ -489,8 +489,8 @@ func (p *Permissions) renderHeader(contentWidth int) string {
 
 func (p *Permissions) renderKeyValue(key, value string, width int) string {
 	t := p.com.Styles
-	keyStyle := t.Muted
-	valueStyle := t.Base
+	keyStyle := t.Dialog.Permissions.KeyText
+	valueStyle := t.Dialog.Permissions.ValueText
 
 	keyStr := keyStyle.Render(key)
 	valueStr := valueStyle.Width(width - lipgloss.Width(keyStr) - 1).Render(" " + value)
@@ -699,7 +699,7 @@ func (p *Permissions) renderDefaultContent(width int) string {
 		if err := json.Unmarshal([]byte(paramStr), &parsed); err == nil {
 			if b, err := json.MarshalIndent(parsed, "", "  "); err == nil {
 				jsonContent := string(b)
-				highlighted, err := common.SyntaxHighlight(t, jsonContent, "params.json", t.BgSubtle)
+				highlighted, err := common.SyntaxHighlight(t, jsonContent, "params.json", t.Dialog.Permissions.ParamsBg)
 				if err == nil {
 					jsonContent = highlighted
 				}

internal/ui/dialog/quit.go 🔗

@@ -96,7 +96,7 @@ func (q *Quit) HandleMsg(msg tea.Msg) Action {
 // Draw implements [Dialog].
 func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
 	const question = "Are you sure you want to quit?"
-	baseStyle := q.com.Styles.Base
+	baseStyle := q.com.Styles.Dialog.Quit.Content
 	buttonOpts := []common.ButtonOpts{
 		{Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
 		{Text: "Nope", Selected: q.selectedNo, Padding: 3},
@@ -111,7 +111,7 @@ func (q *Quit) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
 		),
 	)
 
-	view := q.com.Styles.BorderFocus.Render(content)
+	view := q.com.Styles.Dialog.Quit.Frame.Render(content)
 	DrawCenter(scr, area, view)
 	return nil
 }

internal/ui/dialog/reasoning.go 🔗

@@ -293,8 +293,8 @@ func (r *ReasoningItem) Render(width int) string {
 	styles := ListItemStyles{
 		ItemBlurred:     r.t.Dialog.NormalItem,
 		ItemFocused:     r.t.Dialog.SelectedItem,
-		InfoTextBlurred: r.t.Base,
-		InfoTextFocused: r.t.Base,
+		InfoTextBlurred: r.t.Dialog.ListItem.InfoBlurred,
+		InfoTextFocused: r.t.Dialog.ListItem.InfoFocused,
 	}
 	return renderItem(styles, r.title, info, r.focused, width, r.cache, &r.m)
 }

internal/ui/dialog/sessions_item.go 🔗

@@ -79,8 +79,8 @@ func (s *SessionItem) Render(width int) string {
 	styles := ListItemStyles{
 		ItemBlurred:     s.t.Dialog.NormalItem,
 		ItemFocused:     s.t.Dialog.SelectedItem,
-		InfoTextBlurred: s.t.Subtle,
-		InfoTextFocused: s.t.Base,
+		InfoTextBlurred: s.t.Dialog.Sessions.InfoBlurred,
+		InfoTextFocused: s.t.Dialog.Sessions.InfoFocused,
 	}
 
 	switch s.sessionsMode {

internal/ui/logo/example/main.go 🔗

@@ -20,23 +20,23 @@ func main() {
 
 	s := styles.DefaultStyles()
 	opts := logo.Opts{
-		FieldColor:   s.LogoFieldColor,
-		TitleColorA:  s.LogoTitleColorA,
-		TitleColorB:  s.LogoTitleColorB,
-		CharmColor:   s.LogoCharmColor,
-		VersionColor: s.LogoVersionColor,
+		FieldColor:   s.Logo.FieldColor,
+		TitleColorA:  s.Logo.TitleColorA,
+		TitleColorB:  s.Logo.TitleColorB,
+		CharmColor:   s.Logo.CharmColor,
+		VersionColor: s.Logo.VersionColor,
 		Width:        w,
 		Unstable:     true,
 	}
 
 	renderCompact := func(hyper bool) string {
 		opts.Hyper = hyper
-		return logo.Render(s.Base, "v1.0.0", true, opts)
+		return logo.Render(s.Logo.GradCanvas, "v1.0.0", true, opts)
 	}
 
 	renderWide := func(hyper bool) string {
 		opts.Hyper = hyper
-		return logo.Render(s.Base, "v1.0.0", false, opts)
+		return logo.Render(s.Logo.GradCanvas, "v1.0.0", false, opts)
 	}
 
 	lipgloss.Println(

internal/ui/logo/logo.go 🔗

@@ -149,12 +149,12 @@ func Render(base lipgloss.Style, version string, compact bool, o Opts) string {
 // SmallRender renders a smaller version of the Crush logo, suitable for
 // smaller windows or sidebar usage.
 func SmallRender(t *styles.Styles, width int) string {
-	title := t.Base.Foreground(t.Secondary).Render("Charm™")
-	title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Base, "Crush", t.Secondary, t.Primary))
+	title := t.Logo.SmallCharm.Render("Charm™")
+	title = fmt.Sprintf("%s %s", title, styles.ApplyBoldForegroundGrad(t.Logo.GradCanvas, "Crush", t.Logo.SmallGradFromColor, t.Logo.SmallGradToColor))
 	remainingWidth := width - lipgloss.Width(title) - 1 // 1 for the space after "Crush"
 	if remainingWidth > 0 {
 		lines := strings.Repeat("╱", remainingWidth)
-		title = fmt.Sprintf("%s %s", title, t.Base.Foreground(t.Primary).Render(lines))
+		title = fmt.Sprintf("%s %s", title, t.Logo.SmallDiagonals.Render(lines))
 	}
 	return title
 }

internal/ui/model/header.go 🔗

@@ -39,7 +39,7 @@ func newHeader(com *common.Common) *header {
 	}
 	t := com.Styles
 	h.compactLogo = t.Header.Charm.Render("Charm™") + " " +
-		styles.ApplyBoldForegroundGrad(t.Base, "CRUSH", t.Secondary, t.Primary) + " "
+		styles.ApplyBoldForegroundGrad(t.Header.LogoGradCanvas, "CRUSH", t.Header.LogoGradFromColor, t.Header.LogoGradToColor) + " "
 	return h
 }
 
@@ -102,7 +102,7 @@ func (h *header) drawHeader(
 	b.WriteString(details)
 
 	view := uv.NewStyledString(
-		t.Base.Padding(0, rightPadding, 0, leftPadding).Render(b.String()))
+		t.Header.Wrapper.Padding(0, rightPadding, 0, leftPadding).Render(b.String()))
 	view.Draw(scr, area)
 }
 

internal/ui/model/layout_test.go 🔗

@@ -30,7 +30,7 @@ func newTestUI() *UI {
 	com := common.DefaultCommon(nil)
 
 	ta := textarea.New()
-	ta.SetStyles(com.Styles.TextArea)
+	ta.SetStyles(com.Styles.Editor.Textarea)
 	ta.ShowLineNumbers = false
 	ta.CharLimit = -1
 	ta.SetVirtualCursor(false)

internal/ui/model/lsp.go 🔗

@@ -41,11 +41,11 @@ func (m *UI) lspInfo(width, maxItems int, isSection bool) string {
 		lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
 	}
 
-	title := t.ResourceGroupTitle.Render("LSPs")
+	title := t.Resource.Heading.Render("LSPs")
 	if isSection {
 		title = common.Section(t, title, width)
 	}
-	list := t.ResourceAdditionalText.Render("None")
+	list := t.Resource.AdditionalText.Render("None")
 	if len(lsps) > 0 {
 		list = lspList(t, lsps, width, maxItems)
 	}
@@ -80,31 +80,31 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
 	var renderedLsps []string
 	for _, l := range lsps {
 		var icon string
-		title := t.ResourceName.Render(l.Name)
+		title := t.Resource.Name.Render(l.Name)
 		var description string
 		var diagnostics string
 		switch l.State {
 		case lsp.StateUnstarted:
-			icon = t.ResourceOfflineIcon.String()
-			description = t.ResourceStatus.Render("unstarted")
+			icon = t.Resource.OfflineIcon.String()
+			description = t.Resource.StatusText.Render("unstarted")
 		case lsp.StateStopped:
-			icon = t.ResourceOfflineIcon.String()
-			description = t.ResourceStatus.Render("stopped")
+			icon = t.Resource.OfflineIcon.String()
+			description = t.Resource.StatusText.Render("stopped")
 		case lsp.StateStarting:
-			icon = t.ResourceBusyIcon.String()
-			description = t.ResourceStatus.Render("starting...")
+			icon = t.Resource.BusyIcon.String()
+			description = t.Resource.StatusText.Render("starting...")
 		case lsp.StateReady:
-			icon = t.ResourceOnlineIcon.String()
+			icon = t.Resource.OnlineIcon.String()
 			diagnostics = lspDiagnostics(t, l.Diagnostics)
 		case lsp.StateError:
-			icon = t.ResourceErrorIcon.String()
-			description = t.ResourceStatus.Render("error")
+			icon = t.Resource.ErrorIcon.String()
+			description = t.Resource.StatusText.Render("error")
 			if l.Error != nil {
-				description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", l.Error.Error()))
+				description = t.Resource.StatusText.Render(fmt.Sprintf("error: %s", l.Error.Error()))
 			}
 		case lsp.StateDisabled:
-			icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
-			description = t.ResourceStatus.Render("disabled")
+			icon = t.Resource.DisabledIcon.String()
+			description = t.Resource.StatusText.Render("disabled")
 		default:
 			continue
 		}
@@ -119,7 +119,7 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
 	if len(renderedLsps) > maxItems {
 		visibleItems := renderedLsps[:maxItems-1]
 		remaining := len(renderedLsps) - maxItems
-		visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
+		visibleItems = append(visibleItems, t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
 		return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
 	}
 	return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)

internal/ui/model/mcp.go 🔗

@@ -23,11 +23,11 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
 		}
 	}
 
-	title := t.ResourceGroupTitle.Render("MCPs")
+	title := t.Resource.Heading.Render("MCPs")
 	if isSection {
 		title = common.Section(t, title, width)
 	}
-	list := t.ResourceAdditionalText.Render("None")
+	list := t.Resource.AdditionalText.Render("None")
 	if len(mcps) > 0 {
 		list = mcpList(t, mcps, width, maxItems)
 	}
@@ -39,13 +39,13 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
 func mcpCounts(t *styles.Styles, counts mcp.Counts) string {
 	var parts []string
 	if counts.Tools > 0 {
-		parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d tools", counts.Tools)))
+		parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d tools", counts.Tools)))
 	}
 	if counts.Prompts > 0 {
-		parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
+		parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d prompts", counts.Prompts)))
 	}
 	if counts.Resources > 0 {
-		parts = append(parts, t.Subtle.Render(fmt.Sprintf("%d resources", counts.Resources)))
+		parts = append(parts, t.Resource.CapabilityCount.Render(fmt.Sprintf("%d resources", counts.Resources)))
 	}
 	return strings.Join(parts, " ")
 }
@@ -65,28 +65,28 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
 		if m.Name == config.DockerMCPName {
 			title = "Docker MCP"
 		}
-		title = t.ResourceName.Render(title)
+		title = t.Resource.Name.Render(title)
 		var description string
 		var extraContent string
 
 		switch m.State {
 		case mcp.StateStarting:
-			icon = t.ResourceBusyIcon.String()
-			description = t.ResourceStatus.Render("starting...")
+			icon = t.Resource.BusyIcon.String()
+			description = t.Resource.StatusText.Render("starting...")
 		case mcp.StateConnected:
-			icon = t.ResourceOnlineIcon.String()
+			icon = t.Resource.OnlineIcon.String()
 			extraContent = mcpCounts(t, m.Counts)
 		case mcp.StateError:
-			icon = t.ResourceErrorIcon.String()
-			description = t.ResourceStatus.Render("error")
+			icon = t.Resource.ErrorIcon.String()
+			description = t.Resource.StatusText.Render("error")
 			if m.Error != nil {
-				description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", m.Error.Error()))
+				description = t.Resource.StatusText.Render(fmt.Sprintf("error: %s", m.Error.Error()))
 			}
 		case mcp.StateDisabled:
-			icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
-			description = t.ResourceStatus.Render("disabled")
+			icon = t.Resource.DisabledIcon.String()
+			description = t.Resource.StatusText.Render("disabled")
 		default:
-			icon = t.ResourceOfflineIcon.String()
+			icon = t.Resource.OfflineIcon.String()
 		}
 
 		renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
@@ -100,7 +100,7 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
 	if len(renderedMcps) > maxItems {
 		visibleItems := renderedMcps[:maxItems-1]
 		remaining := len(renderedMcps) - maxItems
-		visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
+		visibleItems = append(visibleItems, t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)))
 		return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
 	}
 	return lipgloss.JoinVertical(lipgloss.Left, renderedMcps...)

internal/ui/model/pills.go 🔗

@@ -56,12 +56,12 @@ func queuePill(queue int, focused, panelFocused bool, t *styles.Styles) string {
 	if queue <= 0 {
 		return ""
 	}
-	triangles := styles.ForegroundGrad(t.Base, "▶▶▶▶▶▶▶▶▶", false, t.Pills.QueueGradFromColor, t.Secondary)
+	triangles := styles.ForegroundGrad(t.Pills.QueueIconBase, "▶▶▶▶▶▶▶▶▶", false, t.Pills.QueueGradFromColor, t.Pills.QueueGradToColor)
 	if queue < len(triangles) {
 		triangles = triangles[:queue]
 	}
 
-	text := t.Base.Render(fmt.Sprintf("%d Queued", queue))
+	text := t.Pills.QueueLabel.Render(fmt.Sprintf("%d Queued", queue))
 	content := fmt.Sprintf("%s %s", strings.Join(triangles, ""), text)
 	return pillStyle(focused, panelFocused, t).Render(content)
 }
@@ -87,8 +87,8 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
 
 	total := len(todos)
 
-	label := t.Base.Render("To-Do")
-	progress := t.Muted.Render(fmt.Sprintf("%d/%d", completed, total))
+	label := t.Pills.TodoLabel.Render("To-Do")
+	progress := t.Pills.TodoProgress.Render(fmt.Sprintf("%d/%d", completed, total))
 
 	var content string
 	if panelFocused {
@@ -101,7 +101,7 @@ func todoPill(todos []session.Todo, spinnerView string, focused, panelFocused bo
 		if len(taskText) > maxTaskDisplayLength {
 			taskText = taskText[:maxTaskDisplayLength-1] + "…"
 		}
-		task := t.Subtle.Render(taskText)
+		task := t.Pills.TodoCurrentTask.Render(taskText)
 		content = fmt.Sprintf("%s %s %s  %s", spinnerView, label, progress, task)
 	} else {
 		content = fmt.Sprintf("%s %s", label, progress)
@@ -128,7 +128,7 @@ func queueList(queueItems []string, t *styles.Styles) string {
 			text = text[:maxQueueDisplayLength-1] + "…"
 		}
 		prefix := t.Pills.QueueItemPrefix.Render() + " "
-		lines = append(lines, prefix+t.Muted.Render(text))
+		lines = append(lines, prefix+t.Pills.QueueItemText.Render(text))
 	}
 
 	return strings.Join(lines, "\n")

internal/ui/model/session.go 🔗

@@ -163,11 +163,11 @@ func (m *UI) handleFileEvent(file history.File) tea.Cmd {
 func (m *UI) filesInfo(cwd string, width, maxItems int, isSection bool) string {
 	t := m.com.Styles
 
-	title := t.Subtle.Render("Modified Files")
+	title := t.Files.SectionTitle.Render("Modified Files")
 	if isSection {
 		title = common.Section(t, "Modified Files", width)
 	}
-	list := t.Subtle.Render("None")
+	list := t.Files.EmptyMessage.Render("None")
 	var filesWithChanges []SessionFile
 	for _, f := range m.sessionFiles {
 		if f.Additions == 0 && f.Deletions == 0 {
@@ -226,7 +226,7 @@ func fileList(t *styles.Styles, cwd string, filesWithChanges []SessionFile, widt
 
 	if len(filesWithChanges) > maxItems {
 		remaining := len(filesWithChanges) - maxItems
-		renderedFiles = append(renderedFiles, t.Subtle.Render(fmt.Sprintf("…and %d more", remaining)))
+		renderedFiles = append(renderedFiles, t.Files.TruncationHint.Render(fmt.Sprintf("…and %d more", remaining)))
 	}
 
 	return lipgloss.JoinVertical(lipgloss.Left, renderedFiles...)

internal/ui/model/sidebar.go 🔗

@@ -138,7 +138,7 @@ func (m *UI) drawSidebar(scr uv.Screen, area uv.Rectangle) {
 	width := area.Dx()
 	height := area.Dy()
 
-	title := t.Muted.Width(width).MaxHeight(2).Render(m.session.Title)
+	title := t.Sidebar.SessionTitle.Width(width).MaxHeight(2).Render(m.session.Title)
 	cwd := common.PrettyPath(t, m.com.Workspace.WorkingDir(), width)
 	sidebarLogo := m.sidebarLogo
 	if height < logoHeightBreakpoint {

internal/ui/model/skills.go 🔗

@@ -38,14 +38,14 @@ func cachedBuiltinSkills() []*skills.Skill {
 func (m *UI) skillsInfo(width, maxItems int, isSection bool) string {
 	t := m.com.Styles
 
-	title := t.ResourceGroupTitle.Render("Skills")
+	title := t.Resource.Heading.Render("Skills")
 	if isSection {
 		title = common.Section(t, title, width)
 	}
 
 	items := m.skillStatusItems()
 	if len(items) == 0 {
-		list := t.ResourceAdditionalText.Render("None")
+		list := t.Resource.AdditionalText.Render("None")
 		return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
 	}
 
@@ -68,14 +68,14 @@ func (m *UI) skillStatusItems() []skillStatusItem {
 			name = filepath.Base(filepath.Dir(state.Path))
 		}
 		stateNames[name] = struct{}{}
-		icon := t.ResourceOnlineIcon.String()
+		icon := t.Resource.OnlineIcon.String()
 		if state.State == skills.StateError {
-			icon = t.ResourceErrorIcon.String()
+			icon = t.Resource.ErrorIcon.String()
 		}
 		items = append(items, skillStatusItem{
 			icon:  icon,
 			name:  name,
-			title: t.ResourceName.Render(name),
+			title: t.Resource.Name.Render(name),
 		})
 	}
 
@@ -88,9 +88,9 @@ func (m *UI) skillStatusItems() []skillStatusItem {
 			continue
 		}
 		items = append(items, skillStatusItem{
-			icon:  t.ResourceOnlineIcon.String(),
+			icon:  t.Resource.OnlineIcon.String(),
 			name:  skill.Name,
-			title: t.ResourceName.Render(skill.Name),
+			title: t.Resource.Name.Render(skill.Name),
 		})
 	}
 
@@ -111,7 +111,7 @@ func skillsList(t *styles.Styles, items []skillStatusItem, width, maxItems int)
 		remaining := len(items) - (maxItems - 1)
 		items = append(visibleItems, skillStatusItem{
 			name:  "more",
-			title: t.ResourceAdditionalText.Render(fmt.Sprintf("…and %d more", remaining)),
+			title: t.Resource.AdditionalText.Render(fmt.Sprintf("…and %d more", remaining)),
 		})
 	}
 

internal/ui/model/skills_test.go 🔗

@@ -28,7 +28,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
 
 	var hasGoDoc bool
 	for _, item := range items {
-		if item.title == st.ResourceName.Render("go-doc") {
+		if item.title == st.Resource.Name.Render("go-doc") {
 			hasGoDoc = true
 			break
 		}
@@ -43,7 +43,7 @@ func TestSkillStatusItemsIncludesBuiltinSkills(t *testing.T) {
 		if skill.Name == "go-doc" {
 			continue
 		}
-		expected := st.ResourceName.Render(skill.Name)
+		expected := st.Resource.Name.Render(skill.Name)
 		for _, item := range items {
 			if item.title == expected {
 				hasBuiltin = true

internal/ui/model/ui.go 🔗

@@ -269,7 +269,7 @@ type UI struct {
 func New(com *common.Common, initialSessionID string, continueLast bool) *UI {
 	// Editor components
 	ta := textarea.New()
-	ta.SetStyles(com.Styles.TextArea)
+	ta.SetStyles(com.Styles.Editor.Textarea)
 	ta.ShowLineNumbers = false
 	ta.CharLimit = -1
 	ta.SetVirtualCursor(false)
@@ -2745,9 +2745,9 @@ func (m *UI) normalPromptFunc(info textarea.PromptInfo) string {
 		return "::: "
 	}
 	if info.Focused {
-		return t.EditorPromptNormalFocused.Render()
+		return t.Editor.PromptNormalFocused.Render()
 	}
-	return t.EditorPromptNormalBlurred.Render()
+	return t.Editor.PromptNormalBlurred.Render()
 }
 
 // yoloPromptFunc returns the yolo mode editor prompt style with warning icon
@@ -2756,15 +2756,15 @@ func (m *UI) yoloPromptFunc(info textarea.PromptInfo) string {
 	t := m.com.Styles
 	if info.LineNumber == 0 {
 		if info.Focused {
-			return t.EditorPromptYoloIconFocused.Render()
+			return t.Editor.PromptYoloIconFocused.Render()
 		} else {
-			return t.EditorPromptYoloIconBlurred.Render()
+			return t.Editor.PromptYoloIconBlurred.Render()
 		}
 	}
 	if info.Focused {
-		return t.EditorPromptYoloDotsFocused.Render()
+		return t.Editor.PromptYoloDotsFocused.Render()
 	}
-	return t.EditorPromptYoloDotsBlurred.Render()
+	return t.Editor.PromptYoloDotsBlurred.Render()
 }
 
 // closeCompletions closes the completions popup and resets state.
@@ -3536,7 +3536,7 @@ func (m *UI) drawSessionDetails(scr uv.Screen, area uv.Rectangle) {
 		blocks...,
 	)
 
-	version := s.CompactDetails.Version.Foreground(s.Border).Width(width).AlignHorizontal(lipgloss.Right).Render(version.Version)
+	version := s.CompactDetails.Version.Width(width).AlignHorizontal(lipgloss.Right).Render(version.Version)
 
 	remainingHeight := height - lipgloss.Height(detailsHeader) - lipgloss.Height(version)
 
@@ -3651,12 +3651,12 @@ func (m *UI) disableDockerMCP() tea.Msg {
 
 // renderLogo renders the Crush logo with the given styles and dimensions.
 func renderLogo(t *styles.Styles, compact bool, width int) string {
-	return logo.Render(t.Base, version.Version, compact, logo.Opts{
-		FieldColor:   t.LogoFieldColor,
-		TitleColorA:  t.LogoTitleColorA,
-		TitleColorB:  t.LogoTitleColorB,
-		CharmColor:   t.LogoCharmColor,
-		VersionColor: t.LogoVersionColor,
+	return logo.Render(t.Logo.GradCanvas, version.Version, compact, logo.Opts{
+		FieldColor:   t.Logo.FieldColor,
+		TitleColorA:  t.Logo.TitleColorA,
+		TitleColorB:  t.Logo.TitleColorB,
+		CharmColor:   t.Logo.CharmColor,
+		VersionColor: t.Logo.VersionColor,
 		Width:        width,
 	})
 }

internal/ui/styles/styles.go 🔗

@@ -58,28 +58,19 @@ const (
 )
 
 type Styles struct {
-	WindowTooSmall lipgloss.Style
-
-	// Reusable text styles
-	Base      lipgloss.Style
-	Muted     lipgloss.Style
-	HalfMuted lipgloss.Style
-	Subtle    lipgloss.Style
-
-	// Tags
-	TagBase  lipgloss.Style
-	TagError lipgloss.Style
-	TagInfo  lipgloss.Style
-
 	// Header
 	Header struct {
-		Charm        lipgloss.Style // Style for "Charm™" label
-		Diagonals    lipgloss.Style // Style for diagonal separators (╱)
-		Percentage   lipgloss.Style // Style for context percentage
-		Keystroke    lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
-		KeystrokeTip lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
-		WorkingDir   lipgloss.Style // Style for current working directory
-		Separator    lipgloss.Style // Style for separator dots (•)
+		Charm             lipgloss.Style // Style for "Charm™" label
+		Diagonals         lipgloss.Style // Style for diagonal separators (╱)
+		Percentage        lipgloss.Style // Style for context percentage
+		Keystroke         lipgloss.Style // Style for keystroke hints (e.g., "ctrl+d")
+		KeystrokeTip      lipgloss.Style // Style for keystroke action text (e.g., "open", "close")
+		WorkingDir        lipgloss.Style // Style for current working directory
+		Separator         lipgloss.Style // Style for separator dots (•)
+		Wrapper           lipgloss.Style // Outer container for the entire header row
+		LogoGradCanvas    lipgloss.Style // Canvas for the compact "CRUSH" gradient
+		LogoGradFromColor color.Color    // "CRUSH" wordmark gradient start
+		LogoGradToColor   color.Color    // "CRUSH" wordmark gradient end
 	}
 
 	CompactDetails struct {
@@ -88,43 +79,18 @@ type Styles struct {
 		Title   lipgloss.Style
 	}
 
-	// Panels
-	PanelMuted lipgloss.Style
-	PanelBase  lipgloss.Style
-
-	// Line numbers for code blocks
-	LineNumber lipgloss.Style
-
-	// Message borders
-	FocusedMessageBorder lipgloss.Border
-
 	// Tool calls
-	ToolCallPending   lipgloss.Style
-	ToolCallError     lipgloss.Style
-	ToolCallSuccess   lipgloss.Style
-	ToolCallCancelled lipgloss.Style
-	EarlyStateMessage lipgloss.Style
+	ToolCallSuccess lipgloss.Style
 
 	// Text selection
 	TextSelection lipgloss.Style
 
-	// LSP and MCP status indicators
-	ResourceGroupTitle     lipgloss.Style
-	ResourceOfflineIcon    lipgloss.Style
-	ResourceBusyIcon       lipgloss.Style
-	ResourceErrorIcon      lipgloss.Style
-	ResourceOnlineIcon     lipgloss.Style
-	ResourceName           lipgloss.Style
-	ResourceStatus         lipgloss.Style
-	ResourceAdditionalText lipgloss.Style
-
 	// Markdown & Chroma
 	Markdown      ansi.StyleConfig
-	PlainMarkdown ansi.StyleConfig
+	QuietMarkdown ansi.StyleConfig
 
 	// Inputs
 	TextInput textinput.Styles
-	TextArea  textarea.Styles
 
 	// Help
 	Help help.Styles
@@ -136,62 +102,55 @@ type Styles struct {
 	FilePicker filepicker.Styles
 
 	// Buttons
-	ButtonFocus lipgloss.Style
-	ButtonBlur  lipgloss.Style
-
-	// Borders
-	BorderFocus lipgloss.Style
-	BorderBlur  lipgloss.Style
+	Button struct {
+		Focused lipgloss.Style
+		Blurred lipgloss.Style
+	}
 
 	// Editor
-	EditorPromptNormalFocused   lipgloss.Style
-	EditorPromptNormalBlurred   lipgloss.Style
-	EditorPromptYoloIconFocused lipgloss.Style
-	EditorPromptYoloIconBlurred lipgloss.Style
-	EditorPromptYoloDotsFocused lipgloss.Style
-	EditorPromptYoloDotsBlurred lipgloss.Style
+	Editor struct {
+		Textarea textarea.Styles
+
+		// Normal mode prompt (default "::: ").
+		PromptNormalFocused lipgloss.Style
+		PromptNormalBlurred lipgloss.Style
+
+		// YOLO mode prompt (" ! " icon + ":::" dots).
+		PromptYoloIconFocused lipgloss.Style
+		PromptYoloIconBlurred lipgloss.Style
+		PromptYoloDotsFocused lipgloss.Style
+		PromptYoloDotsBlurred lipgloss.Style
+	}
 
 	// Radio
-	RadioOn  lipgloss.Style
-	RadioOff lipgloss.Style
+	Radio struct {
+		On    lipgloss.Style
+		Off   lipgloss.Style
+		Label lipgloss.Style // Text next to a radio button
+	}
 
 	// Background
 	Background color.Color
 
 	// Logo
-	LogoFieldColor   color.Color
-	LogoTitleColorA  color.Color
-	LogoTitleColorB  color.Color
-	LogoCharmColor   color.Color
-	LogoVersionColor color.Color
-
-	// Colors - semantic colors for tool rendering.
-	Primary       color.Color
-	Secondary     color.Color
-	Tertiary      color.Color
-	BgBase        color.Color
-	BgBaseLighter color.Color
-	BgSubtle      color.Color
-	BgOverlay     color.Color
-	FgBase        color.Color
-	FgMuted       color.Color
-	FgHalfMuted   color.Color
-	FgSubtle      color.Color
-	Border        color.Color
-	BorderColor   color.Color // Border focus color
-	Error         color.Color
-	Warning       color.Color
-	Info          color.Color
-	White         color.Color
-	BlueLight     color.Color
-	Blue          color.Color
-	BlueDark      color.Color
-	GreenLight    color.Color
-	Green         color.Color
-	GreenDark     color.Color
-	Red           color.Color
-	RedDark       color.Color
-	Yellow        color.Color
+	Logo struct {
+		FieldColor         color.Color
+		TitleColorA        color.Color
+		TitleColorB        color.Color
+		CharmColor         color.Color
+		VersionColor       color.Color
+		SmallCharm         lipgloss.Style // "Charm™" label in SmallRender
+		SmallDiagonals     lipgloss.Style // Diagonal line fill in SmallRender
+		GradCanvas         lipgloss.Style // Blank canvas for gradient painting
+		SmallGradFromColor color.Color    // Small "Crush" wordmark gradient start
+		SmallGradToColor   color.Color    // Small "Crush" wordmark gradient end
+	}
+
+	// Working indicator gradient (spinners/shimmers on assistant "thinking",
+	// tool-call pending, CLI generating, startup).
+	WorkingGradFromColor color.Color
+	WorkingGradToColor   color.Color
+	WorkingLabelColor    color.Color // Label text color next to the indicator
 
 	// Section Title
 	Section struct {
@@ -214,41 +173,80 @@ type Styles struct {
 		InfoDiagnostic    lipgloss.Style
 	}
 
+	// Sidebar
+	Sidebar struct {
+		SessionTitle lipgloss.Style // Current session title at top of sidebar
+		WorkingDir   lipgloss.Style // Working directory path (PrettyPath)
+	}
+
+	// ModelInfo (model name, provider, reasoning, token/cost summary)
+	ModelInfo struct {
+		Icon             lipgloss.Style // Model icon (◇)
+		Name             lipgloss.Style // Model name text
+		Provider         lipgloss.Style // "via <provider>" text
+		ProviderFallback lipgloss.Style // Provider on its own second line
+		Reasoning        lipgloss.Style // Reasoning effort text
+		TokenCount       lipgloss.Style // "(42K)" token count
+		TokenPercentage  lipgloss.Style // "42%" percent of context window
+		Cost             lipgloss.Style // "$0.42" cost readout
+	}
+
+	// Resource styles the LSP/MCP/skills sidebar lists: their heading,
+	// each row's status icon, name, status text, and truncation hints.
+	Resource struct {
+		Heading         lipgloss.Style // Section header ("LSPs", "MCPs", "Skills")
+		Name            lipgloss.Style // Resource name (e.g. "gopls")
+		StatusText      lipgloss.Style // Row status description (e.g. "starting...")
+		OfflineIcon     lipgloss.Style // Offline/unstarted/stopped status icon
+		DisabledIcon    lipgloss.Style // Disabled status icon
+		BusyIcon        lipgloss.Style // Busy/starting status icon
+		ErrorIcon       lipgloss.Style // Error status icon
+		OnlineIcon      lipgloss.Style // Online/ready status icon
+		AdditionalText  lipgloss.Style // "None" and "…and N more" text
+		CapabilityCount lipgloss.Style // "N tools" / "N prompts" / "N resources"
+		RowTitleBase    lipgloss.Style // Base style applied over row titles in common.Status
+		RowDescBase     lipgloss.Style // Base style applied over row descriptions in common.Status
+		DefaultTitleFg  color.Color    // Default title color when opt is zero
+		DefaultDescFg   color.Color    // Default description color when opt is zero
+	}
+
 	// Files
 	Files struct {
-		Path      lipgloss.Style
-		Additions lipgloss.Style
-		Deletions lipgloss.Style
+		Path           lipgloss.Style
+		Additions      lipgloss.Style
+		Deletions      lipgloss.Style
+		SectionTitle   lipgloss.Style // "Modified Files" heading
+		EmptyMessage   lipgloss.Style // "None" placeholder when no files
+		TruncationHint lipgloss.Style // "…and N more" message
 	}
 
 	// Chat
-	Chat struct {
-		// Message item styles
-		Message struct {
-			UserBlurred      lipgloss.Style
-			UserFocused      lipgloss.Style
-			AssistantBlurred lipgloss.Style
-			AssistantFocused lipgloss.Style
-			NoContent        lipgloss.Style
-			Thinking         lipgloss.Style
-			ErrorTag         lipgloss.Style
-			ErrorTitle       lipgloss.Style
-			ErrorDetails     lipgloss.Style
-			ToolCallFocused  lipgloss.Style
-			ToolCallCompact  lipgloss.Style
-			ToolCallBlurred  lipgloss.Style
-			SectionHeader    lipgloss.Style
-
-			// Thinking section styles
-			ThinkingBox            lipgloss.Style // Background for thinking content
-			ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
-			ThinkingFooterTitle    lipgloss.Style // "Thought for" text
-			ThinkingFooterDuration lipgloss.Style // Duration value
-			AssistantInfoIcon      lipgloss.Style
-			AssistantInfoModel     lipgloss.Style
-			AssistantInfoProvider  lipgloss.Style
-			AssistantInfoDuration  lipgloss.Style
-		}
+	// Messages - chat message item styles
+	Messages struct {
+		UserBlurred      lipgloss.Style
+		UserFocused      lipgloss.Style
+		AssistantBlurred lipgloss.Style
+		AssistantFocused lipgloss.Style
+		NoContent        lipgloss.Style
+		Thinking         lipgloss.Style
+		ErrorTag         lipgloss.Style
+		ErrorTitle       lipgloss.Style
+		ErrorDetails     lipgloss.Style
+		ToolCallFocused  lipgloss.Style
+		ToolCallCompact  lipgloss.Style
+		ToolCallBlurred  lipgloss.Style
+		SectionHeader    lipgloss.Style
+
+		// Thinking section styles
+		ThinkingBox            lipgloss.Style // Background for thinking content
+		ThinkingTruncationHint lipgloss.Style // "… (N lines hidden)" hint
+		ThinkingFooterTitle    lipgloss.Style // "Thought for" text
+		ThinkingFooterDuration lipgloss.Style // Duration value
+		AssistantInfoIcon      lipgloss.Style
+		AssistantInfoModel     lipgloss.Style
+		AssistantInfoProvider  lipgloss.Style
+		AssistantInfoDuration  lipgloss.Style
+		AssistantCanceled      lipgloss.Style // Italic "Canceled" footer
 	}
 
 	// Tool - styles for tool call rendering
@@ -316,6 +314,9 @@ type Styles struct {
 		TodoCompletedIcon  lipgloss.Style // Completed todo icon
 		TodoInProgressIcon lipgloss.Style // In-progress todo icon
 		TodoPendingIcon    lipgloss.Style // Pending todo icon
+		TodoStatusNote     lipgloss.Style // " · completed N" / " · starting task" trailing note
+		TodoItem           lipgloss.Style // Default body text for todo list items
+		TodoJustStarted    lipgloss.Style // Text of the just-started todo in tool-call bodies
 
 		// MCP tools
 		MCPName     lipgloss.Style // The mcp name
@@ -329,17 +330,26 @@ type Styles struct {
 		ResourceSize            lipgloss.Style
 		MediaType               lipgloss.Style
 
-		// Docker MCP tools
-		DockerMCPActionAdd lipgloss.Style // Docker MCP add action (green)
-		DockerMCPActionDel lipgloss.Style // Docker MCP remove action (red)
+		// Action verb colors for tool-call headers.
+		ActionCreate  lipgloss.Style // Constructive actions (e.g. "Add", "Create")
+		ActionDestroy lipgloss.Style // Destructive actions (e.g. "Remove", "Delete")
+
+		// Tool result helpers.
+		ResultEmpty      lipgloss.Style // "No results" placeholder
+		ResultTruncation lipgloss.Style // "… and N more" truncation line
+		ResultItemName   lipgloss.Style // Item name (left column in result lists)
+		ResultItemDesc   lipgloss.Style // Item description (right column)
 	}
 
 	// Dialog styles
 	Dialog struct {
-		Title       lipgloss.Style
-		TitleText   lipgloss.Style
-		TitleError  lipgloss.Style
-		TitleAccent lipgloss.Style
+		Title              lipgloss.Style
+		TitleText          lipgloss.Style
+		TitleError         lipgloss.Style
+		TitleAccent        lipgloss.Style
+		TitleLineBase      lipgloss.Style // Base for the gradient ╱╱╱ next to dialog titles
+		TitleGradFromColor color.Color    // Default dialog title ╱╱╱ gradient start
+		TitleGradToColor   color.Color    // Default dialog title ╱╱╱ gradient end
 		// View is the main content area style.
 		View          lipgloss.Style
 		PrimaryText   lipgloss.Style
@@ -381,7 +391,43 @@ type Styles struct {
 			InputRequiredMarkFocused lipgloss.Style
 		}
 
-		Commands struct{}
+		// ListItem styles the info-text rendered alongside list items (commands,
+		// models, reasoning options). Sessions have their own overrides below.
+		ListItem struct {
+			InfoBlurred lipgloss.Style
+			InfoFocused lipgloss.Style
+		}
+
+		Models struct {
+			ConfiguredText lipgloss.Style // "Configured" badge shown on the ModelGroup header
+		}
+
+		Permissions struct {
+			KeyText   lipgloss.Style // Left key cell of a key/value row
+			ValueText lipgloss.Style // Right value cell of a key/value row
+			ParamsBg  color.Color    // Background color behind highlighted JSON parameters
+		}
+
+		Quit struct {
+			Content lipgloss.Style // Wrapper for the quit dialog's inner content
+			Frame   lipgloss.Style // Outer rounded border framing the quit dialog
+		}
+
+		APIKey struct {
+			Spinner lipgloss.Style // Loading spinner while validating the key
+		}
+
+		OAuth struct {
+			Spinner      lipgloss.Style // Loading spinner
+			Instructions lipgloss.Style // Emphasized instruction text
+			UserCode     lipgloss.Style // Prominent user code display
+			Success      lipgloss.Style // Positive status text (e.g. "Authentication successful!")
+			Link         lipgloss.Style // Underlined verification URL
+			Enter        lipgloss.Style // "enter" keyword highlight in instructions
+			ErrorText    lipgloss.Style // Error message when authentication fails
+			StatusText   lipgloss.Style // Narrative status text ("Initializing...", "Verifying...", etc.)
+			UserCodeBg   color.Color    // Background color of the centered user-code box
+		}
 
 		ImagePreview lipgloss.Style
 
@@ -404,6 +450,9 @@ type Styles struct {
 			RenamingTitleGradientFromColor color.Color
 			RenamingTitleGradientToColor   color.Color
 			RenamingPlaceholder            lipgloss.Style
+
+			InfoBlurred lipgloss.Style // Timestamp text on unfocused session items
+			InfoFocused lipgloss.Style // Timestamp text on the focused session item
 		}
 	}
 
@@ -441,14 +490,22 @@ type Styles struct {
 
 	// Pills styles for todo/queue pills
 	Pills struct {
-		Base            lipgloss.Style // Base pill style with padding
-		Focused         lipgloss.Style // Focused pill with visible border
-		Blurred         lipgloss.Style // Blurred pill with hidden border
-		QueueItemPrefix lipgloss.Style // Prefix for queue list items
-		HelpKey         lipgloss.Style // Keystroke hint style
-		HelpText        lipgloss.Style // Help action text style
-		Area            lipgloss.Style // Pills area container
-		TodoSpinner     lipgloss.Style // Todo spinner style
+		Base               lipgloss.Style // Base pill style with padding
+		Focused            lipgloss.Style // Focused pill with visible border
+		Blurred            lipgloss.Style // Blurred pill with hidden border
+		QueueItemPrefix    lipgloss.Style // Prefix for queue list items
+		QueueItemText      lipgloss.Style // Queue list item body text
+		QueueLabel         lipgloss.Style // "N Queued" label text
+		QueueIconBase      lipgloss.Style // Base style for queue gradient triangles
+		QueueGradFromColor color.Color    // Start color for queue indicator gradient
+		QueueGradToColor   color.Color    // End color for queue indicator gradient
+		TodoLabel          lipgloss.Style // "To-Do" label
+		TodoProgress       lipgloss.Style // Todo ratio (e.g. "2/5")
+		TodoCurrentTask    lipgloss.Style // Current in-progress task name
+		TodoSpinner        lipgloss.Style // Todo spinner style
+		HelpKey            lipgloss.Style // Keystroke hint style
+		HelpText           lipgloss.Style // Help action text style
+		Area               lipgloss.Style // Pills area container
 	}
 }
 
@@ -552,38 +609,17 @@ func DefaultStyles() Styles {
 	normalBorder := lipgloss.NormalBorder()
 
 	base := lipgloss.NewStyle().Foreground(fgBase)
+	muted := lipgloss.NewStyle().Foreground(fgMuted)
+	subtle := lipgloss.NewStyle().Foreground(fgSubtle)
 
 	s := Styles{}
 
 	s.Background = bgBase
 
 	// Populate color fields
-	s.Primary = primary
-	s.Secondary = secondary
-	s.Tertiary = tertiary
-	s.BgBase = bgBase
-	s.BgBaseLighter = bgBaseLighter
-	s.BgSubtle = bgSubtle
-	s.BgOverlay = bgOverlay
-	s.FgBase = fgBase
-	s.FgMuted = fgMuted
-	s.FgHalfMuted = fgHalfMuted
-	s.FgSubtle = fgSubtle
-	s.Border = border
-	s.BorderColor = borderFocus
-	s.Error = error
-	s.Warning = warning
-	s.Info = info
-	s.White = white
-	s.BlueLight = blueLight
-	s.Blue = blue
-	s.BlueDark = blueDark
-	s.GreenLight = greenLight
-	s.Green = green
-	s.GreenDark = greenDark
-	s.Red = red
-	s.RedDark = redDark
-	s.Yellow = yellow
+	s.WorkingGradFromColor = primary
+	s.WorkingGradToColor = secondary
+	s.WorkingLabelColor = fgBase
 
 	s.TextInput = textinput.Styles{
 		Focused: textinput.StyleState{
@@ -605,7 +641,7 @@ func DefaultStyles() Styles {
 		},
 	}
 
-	s.TextArea = textarea.Styles{
+	s.Editor.Textarea = textarea.Styles{
 		Focused: textarea.StyleState{
 			Base:             base,
 			Text:             base,
@@ -840,10 +876,10 @@ func DefaultStyles() Styles {
 		},
 	}
 
-	// PlainMarkdown style - muted colors on subtle background for thinking content.
+	// QuietMarkdown style - muted colors on subtle background for thinking content.
 	plainBg := new(bgBaseLighter.Hex())
 	plainFg := new(fgMuted.Hex())
-	s.PlainMarkdown = ansi.StyleConfig{
+	s.QuietMarkdown = ansi.StyleConfig{
 		Document: ansi.StyleBlock{
 			StylePrimitive: ansi.StylePrimitive{
 				Color:           plainFg,
@@ -1081,72 +1117,47 @@ func DefaultStyles() Styles {
 	}
 
 	// borders
-	s.FocusedMessageBorder = lipgloss.Border{Left: BorderThick}
-
-	// text presets
-	s.Base = lipgloss.NewStyle().Foreground(fgBase)
-	s.Muted = lipgloss.NewStyle().Foreground(fgMuted)
-	s.HalfMuted = lipgloss.NewStyle().Foreground(fgHalfMuted)
-	s.Subtle = lipgloss.NewStyle().Foreground(fgSubtle)
-
-	s.WindowTooSmall = s.Muted
-
-	// tag presets
-	s.TagBase = lipgloss.NewStyle().Padding(0, 1).Foreground(white)
-	s.TagError = s.TagBase.Background(redDark)
-	s.TagInfo = s.TagBase.Background(blueLight)
+	s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
 
-	// Compact header styles
 	s.Header.Charm = base.Foreground(secondary)
 	s.Header.Diagonals = base.Foreground(primary)
-	s.Header.Percentage = s.Muted
-	s.Header.Keystroke = s.Muted
-	s.Header.KeystrokeTip = s.Subtle
-	s.Header.WorkingDir = s.Muted
-	s.Header.Separator = s.Subtle
-
-	s.CompactDetails.Title = s.Base
-	s.CompactDetails.View = s.Base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
-	s.CompactDetails.Version = s.Muted
-
-	// panels
-	s.PanelMuted = s.Muted.Background(bgBaseLighter)
-	s.PanelBase = lipgloss.NewStyle().Background(bgBase)
-
-	// code line number
-	s.LineNumber = lipgloss.NewStyle().Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
-
-	// Tool calls
-	s.ToolCallPending = lipgloss.NewStyle().Foreground(greenDark).SetString(ToolPending)
-	s.ToolCallError = lipgloss.NewStyle().Foreground(redDark).SetString(ToolError)
-	s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
-	// Cancelled uses muted tone but same glyph as pending
-	s.ToolCallCancelled = s.Muted.SetString(ToolPending)
-	s.EarlyStateMessage = s.Subtle.PaddingLeft(2)
+	s.Header.Percentage = muted
+	s.Header.Keystroke = muted
+	s.Header.KeystrokeTip = subtle
+	s.Header.WorkingDir = muted
+	s.Header.Separator = subtle
+	s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
+	s.Header.LogoGradCanvas = lipgloss.NewStyle()
+	s.Header.LogoGradFromColor = secondary
+	s.Header.LogoGradToColor = primary
+
+	s.CompactDetails.Title = base
+	s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
+	s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
 
 	// Tool rendering styles
 	s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
 	s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
 	s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
-	s.Tool.IconCancelled = s.Muted.SetString(ToolPending)
+	s.Tool.IconCancelled = muted.SetString(ToolPending)
 
 	s.Tool.NameNormal = base.Foreground(blue)
 	s.Tool.NameNested = base.Foreground(blue)
 
-	s.Tool.ParamMain = s.Subtle
-	s.Tool.ParamKey = s.Subtle
+	s.Tool.ParamMain = subtle
+	s.Tool.ParamKey = subtle
 
 	// Content rendering - prepared styles that accept width parameter
-	s.Tool.ContentLine = s.Muted.Background(bgBaseLighter)
-	s.Tool.ContentTruncation = s.Muted.Background(bgBaseLighter)
-	s.Tool.ContentCodeLine = s.Base.Background(bgBase).PaddingLeft(2)
-	s.Tool.ContentCodeTruncation = s.Muted.Background(bgBase).PaddingLeft(2)
+	s.Tool.ContentLine = muted.Background(bgBaseLighter)
+	s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
+	s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
+	s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
 	s.Tool.ContentCodeBg = bgBase
 	s.Tool.Body = base.PaddingLeft(2)
 
 	// Deprecated - kept for backward compatibility
-	s.Tool.ContentBg = s.Muted.Background(bgBaseLighter)
-	s.Tool.ContentText = s.Muted
+	s.Tool.ContentBg = muted.Background(bgBaseLighter)
+	s.Tool.ContentText = muted
 	s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
 
 	s.Tool.StateWaiting = base.Foreground(fgSubtle)
@@ -1156,7 +1167,7 @@ func DefaultStyles() Styles {
 	s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
 
 	// Diff and multi-edit styles
-	s.Tool.DiffTruncation = s.Muted.Background(bgBaseLighter).PaddingLeft(2)
+	s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
 	s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
 	s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
 
@@ -1166,12 +1177,12 @@ func DefaultStyles() Styles {
 	s.Tool.JobIconSuccess = base.Foreground(green)
 	s.Tool.JobToolName = base.Foreground(blue)
 	s.Tool.JobAction = base.Foreground(blueDark)
-	s.Tool.JobPID = s.Muted
-	s.Tool.JobDescription = s.Subtle
+	s.Tool.JobPID = muted
+	s.Tool.JobDescription = subtle
 
 	// Agent task styles
 	s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
-	s.Tool.AgentPrompt = s.Muted
+	s.Tool.AgentPrompt = muted
 
 	// Agentic fetch styles
 	s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
@@ -1181,6 +1192,9 @@ func DefaultStyles() Styles {
 	s.Tool.TodoCompletedIcon = base.Foreground(green)
 	s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
 	s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
+	s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
+	s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
 
 	// MCP styles
 	s.Tool.MCPName = base.Foreground(blue)
@@ -1194,103 +1208,138 @@ func DefaultStyles() Styles {
 	s.Tool.MediaType = base
 	s.Tool.ResourceSize = base.Foreground(fgMuted)
 
-	// Docker MCP styles
-	s.Tool.DockerMCPActionAdd = base.Foreground(greenLight)
-	s.Tool.DockerMCPActionDel = base.Foreground(red)
+	// Tool-call action verbs and result-list styling.
+	s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(greenLight)
+	s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(red)
+	s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
+	s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
 
 	// Buttons
-	s.ButtonFocus = lipgloss.NewStyle().Foreground(white).Background(secondary)
-	s.ButtonBlur = s.Base.Background(bgSubtle)
-
-	// Borders
-	s.BorderFocus = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
+	s.Button.Focused = lipgloss.NewStyle().Foreground(white).Background(secondary)
+	s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
 
 	// Editor
-	s.EditorPromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
-	s.EditorPromptNormalBlurred = s.EditorPromptNormalFocused.Foreground(fgMuted)
-	s.EditorPromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
-	s.EditorPromptYoloIconBlurred = s.EditorPromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
-	s.EditorPromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
-	s.EditorPromptYoloDotsBlurred = s.EditorPromptYoloDotsFocused.Foreground(charmtone.Squid)
-
-	s.RadioOn = s.HalfMuted.SetString(RadioOn)
-	s.RadioOff = s.HalfMuted.SetString(RadioOff)
-
-	// Logo colors
-	s.LogoFieldColor = primary
-	s.LogoTitleColorA = secondary
-	s.LogoTitleColorB = primary
-	s.LogoCharmColor = secondary
-	s.LogoVersionColor = primary
+	s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
+	s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
+	s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
+	s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
+	s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
+	s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(charmtone.Squid)
+
+	s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
+	s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
+	s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
+
+	// Logo
+	s.Logo.FieldColor = primary
+	s.Logo.TitleColorA = secondary
+	s.Logo.TitleColorB = primary
+	s.Logo.CharmColor = secondary
+	s.Logo.VersionColor = primary
+	s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
+	s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
+	s.Logo.GradCanvas = lipgloss.NewStyle()
+	s.Logo.SmallGradFromColor = secondary
+	s.Logo.SmallGradToColor = primary
 
 	// Section
-	s.Section.Title = s.Subtle
-	s.Section.Line = s.Base.Foreground(charmtone.Charcoal)
+	s.Section.Title = subtle
+	s.Section.Line = base.Foreground(charmtone.Charcoal)
 
 	// Initialize
-	s.Initialize.Header = s.Base
-	s.Initialize.Content = s.Muted
-	s.Initialize.Accent = s.Base.Foreground(greenDark)
-
-	// LSP and MCP status.
-	s.ResourceGroupTitle = lipgloss.NewStyle().Foreground(charmtone.Oyster)
-	s.ResourceOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
-	s.ResourceBusyIcon = s.ResourceOfflineIcon.Foreground(charmtone.Citron)
-	s.ResourceErrorIcon = s.ResourceOfflineIcon.Foreground(charmtone.Coral)
-	s.ResourceOnlineIcon = s.ResourceOfflineIcon.Foreground(charmtone.Guac)
-	s.ResourceName = lipgloss.NewStyle().Foreground(charmtone.Squid)
-	s.ResourceStatus = lipgloss.NewStyle().Foreground(charmtone.Oyster)
-	s.ResourceAdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+	s.Initialize.Header = base
+	s.Initialize.Content = muted
+	s.Initialize.Accent = base.Foreground(greenDark)
+
+	// ResourceGroup (LSP/MCP/skills sidebar lists).
+	s.Resource.Heading = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+	s.Resource.Name = lipgloss.NewStyle().Foreground(charmtone.Squid)
+	s.Resource.StatusText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+	s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
+	s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(charmtone.Citron)
+	s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(charmtone.Coral)
+	s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(charmtone.Guac)
+	s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
+	s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+	s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
+	s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
+	s.Resource.DefaultTitleFg = fgMuted
+	s.Resource.DefaultDescFg = fgSubtle
 
 	// LSP
-	s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)
-	s.LSP.WarningDiagnostic = s.Base.Foreground(warning)
-	s.LSP.HintDiagnostic = s.Base.Foreground(fgHalfMuted)
-	s.LSP.InfoDiagnostic = s.Base.Foreground(info)
+	s.LSP.ErrorDiagnostic = base.Foreground(redDark)
+	s.LSP.WarningDiagnostic = base.Foreground(warning)
+	s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
+	s.LSP.InfoDiagnostic = base.Foreground(info)
 
 	// Files
-	s.Files.Path = s.Muted
-	s.Files.Additions = s.Base.Foreground(greenDark)
-	s.Files.Deletions = s.Base.Foreground(redDark)
+	s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Files.Additions = lipgloss.NewStyle().Foreground(greenDark)
+	s.Files.Deletions = lipgloss.NewStyle().Foreground(redDark)
+	s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
+
+	// Sidebar
+	s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
+
+	// ModelInfo
+	s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
+	s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
+	s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
+	s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
+	s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
+	s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
+
+	// ResourceGroup
+	s.Resource.DefaultTitleFg = fgMuted
+	s.Resource.DefaultDescFg = fgSubtle
 
 	// Chat
 	messageFocussedBorder := lipgloss.Border{
 		Left: "▌",
 	}
 
-	s.Chat.Message.NoContent = lipgloss.NewStyle().Foreground(fgBase)
-	s.Chat.Message.UserBlurred = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
+	s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
+	s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
 		BorderForeground(primary).BorderStyle(normalBorder)
-	s.Chat.Message.UserFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
+	s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
 		BorderForeground(primary).BorderStyle(messageFocussedBorder)
-	s.Chat.Message.AssistantBlurred = s.Chat.Message.NoContent.PaddingLeft(2)
-	s.Chat.Message.AssistantFocused = s.Chat.Message.NoContent.PaddingLeft(1).BorderLeft(true).
+	s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
+	s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
 		BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
-	s.Chat.Message.Thinking = lipgloss.NewStyle().MaxHeight(10)
-	s.Chat.Message.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
+	s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
+	s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
 		Background(red).Foreground(white)
-	s.Chat.Message.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
-	s.Chat.Message.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
+	s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
 
 	// Message item styles
-	s.Chat.Message.ToolCallFocused = s.Muted.PaddingLeft(1).
+	s.Messages.ToolCallFocused = muted.PaddingLeft(1).
 		BorderStyle(messageFocussedBorder).
 		BorderLeft(true).
 		BorderForeground(greenDark)
-	s.Chat.Message.ToolCallBlurred = s.Muted.PaddingLeft(2)
+	s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
 	// No padding or border for compact tool calls within messages
-	s.Chat.Message.ToolCallCompact = s.Muted
-	s.Chat.Message.SectionHeader = s.Base.PaddingLeft(2)
-	s.Chat.Message.AssistantInfoIcon = s.Subtle
-	s.Chat.Message.AssistantInfoModel = s.Muted
-	s.Chat.Message.AssistantInfoProvider = s.Subtle
-	s.Chat.Message.AssistantInfoDuration = s.Subtle
+	s.Messages.ToolCallCompact = muted
+	s.Messages.SectionHeader = base.PaddingLeft(2)
+	s.Messages.AssistantInfoIcon = subtle
+	s.Messages.AssistantInfoModel = muted
+	s.Messages.AssistantInfoProvider = subtle
+	s.Messages.AssistantInfoDuration = subtle
+	s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
 
 	// Thinking section styles
-	s.Chat.Message.ThinkingBox = s.Subtle.Background(bgBaseLighter)
-	s.Chat.Message.ThinkingTruncationHint = s.Muted
-	s.Chat.Message.ThinkingFooterTitle = s.Muted
-	s.Chat.Message.ThinkingFooterDuration = s.Subtle
+	s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
+	s.Messages.ThinkingTruncationHint = muted
+	s.Messages.ThinkingFooterTitle = muted
+	s.Messages.ThinkingFooterDuration = subtle
 
 	// Text selection.
 	s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
@@ -1300,6 +1349,25 @@ func DefaultStyles() Styles {
 	s.Dialog.TitleText = base.Foreground(primary)
 	s.Dialog.TitleError = base.Foreground(red)
 	s.Dialog.TitleAccent = base.Foreground(green).Bold(true)
+	s.Dialog.TitleLineBase = lipgloss.NewStyle()
+	s.Dialog.TitleGradFromColor = primary
+	s.Dialog.TitleGradToColor = secondary
+
+	// Dialog.ListItem (commands, reasoning, models)
+	s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
+	s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
+
+	// Dialog.Models
+	s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
+
+	// Dialog.Permissions
+	s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
+	s.Dialog.Permissions.ParamsBg = bgSubtle
+
+	// Dialog.Quit
+	s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
+	s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
 	s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
 	s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
 	s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
@@ -1323,6 +1391,20 @@ func DefaultStyles() Styles {
 
 	s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
 
+	// API key input dialog
+	s.Dialog.APIKey.Spinner = base.Foreground(green)
+
+	// OAuth dialog
+	s.Dialog.OAuth.Spinner = base.Foreground(greenLight)
+	s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(white)
+	s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(white)
+	s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(greenLight)
+	s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(greenDark).Underline(true)
+	s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
+	s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
+	s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Dialog.OAuth.UserCodeBg = bgBaseLighter
+
 	s.Dialog.Arguments.Content = base.Padding(1)
 	s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
 	s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
@@ -1332,20 +1414,22 @@ func DefaultStyles() Styles {
 
 	s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
 	s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(red)
-	s.Dialog.Sessions.DeletingMessage = s.Base.Padding(1)
+	s.Dialog.Sessions.DeletingMessage = base.Padding(1)
 	s.Dialog.Sessions.DeletingTitleGradientFromColor = red
-	s.Dialog.Sessions.DeletingTitleGradientToColor = s.Primary
+	s.Dialog.Sessions.DeletingTitleGradientToColor = primary
 	s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
 	s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
 
 	s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
 	s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(charmtone.Zest)
-	s.Dialog.Sessions.RenamingingMessage = s.Base.Padding(1)
+	s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
 	s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
 	s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
 	s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
 	s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
 	s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(charmtone.Squid)
+	s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
 
 	s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
 	s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
@@ -1375,11 +1459,19 @@ func DefaultStyles() Styles {
 	s.Pills.Base = base.Padding(0, 1)
 	s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
 	s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
-	s.Pills.QueueItemPrefix = s.Muted.SetString("  •")
-	s.Pills.HelpKey = s.Muted
-	s.Pills.HelpText = s.Subtle
+	s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString("  •")
+	s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
+	s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
+	s.Pills.QueueGradFromColor = redDark
+	s.Pills.QueueGradToColor = secondary
+	s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
+	s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
+	s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(greenDark)
+	s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
+	s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
 	s.Pills.Area = base
-	s.Pills.TodoSpinner = base.Foreground(greenDark)
 
 	return s
 }