@cat-factory/app 0.308.0 → 0.309.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +21 -0
  2. package/app/assets/css/main.css +22 -0
  3. package/app/assets/css/prose.css +4 -4
  4. package/app/assets/css/type.css +24 -0
  5. package/app/components/assistant/AssistantModal.vue +3 -2
  6. package/app/components/auth/UserMenu.vue +1 -1
  7. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +15 -15
  8. package/app/components/binaryOutput/BinaryOutputReport.vue +10 -14
  9. package/app/components/binaryOutput/StoredAssetView.vue +5 -5
  10. package/app/components/board/AddTaskModal.vue +16 -14
  11. package/app/components/board/AgentFailureCard.vue +9 -9
  12. package/app/components/board/AgentFailureHistory.vue +1 -1
  13. package/app/components/board/CreateInitiativeModal.vue +2 -2
  14. package/app/components/board/FailureDetail.vue +1 -1
  15. package/app/components/board/FailureHistoryList.vue +5 -5
  16. package/app/components/board/RecurringPipelineModal.vue +12 -11
  17. package/app/components/board/ReviewFrictionDialog.vue +4 -3
  18. package/app/components/board/StepExecutionHistory.vue +8 -8
  19. package/app/components/board/nodes/AgentChip.vue +1 -1
  20. package/app/components/board/nodes/BlockNode.vue +8 -9
  21. package/app/components/board/nodes/EpicNode.vue +4 -4
  22. package/app/components/board/nodes/FrameSwimlanes.vue +4 -7
  23. package/app/components/board/nodes/InitiativeCard.vue +4 -4
  24. package/app/components/board/nodes/LaneGroup.vue +3 -5
  25. package/app/components/board/nodes/TaskCard.vue +8 -8
  26. package/app/components/board/nodes/TaskLane.vue +3 -3
  27. package/app/components/board/nodes/TaskPipelineMini.vue +13 -12
  28. package/app/components/bootstrap/AdoptionReviewModal.vue +7 -6
  29. package/app/components/bootstrap/BootstrapModal.vue +11 -10
  30. package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
  31. package/app/components/brainstorm/BrainstormWindow.vue +8 -7
  32. package/app/components/bugFishing/BugFishingWindow.vue +37 -36
  33. package/app/components/clarity/ClarityReviewWindow.vue +8 -7
  34. package/app/components/common/AsyncViewError.vue +1 -1
  35. package/app/components/common/ClarificationItem.vue +6 -6
  36. package/app/components/common/DescriptorFields.vue +1 -1
  37. package/app/components/common/EmptyState.vue +1 -1
  38. package/app/components/common/InterviewGateNotice.vue +4 -4
  39. package/app/components/common/MarkdownProse.vue +8 -2
  40. package/app/components/common/SectionLabel.vue +38 -0
  41. package/app/components/consensus/ConsensusSessionWindow.vue +10 -9
  42. package/app/components/context/ContextAttachmentFields.vue +9 -8
  43. package/app/components/docs/DocInterviewWindow.vue +10 -9
  44. package/app/components/documents/ContextDocumentPicker.vue +10 -12
  45. package/app/components/documents/DocumentImportModal.vue +3 -2
  46. package/app/components/documents/DocumentSourceConnectModal.vue +2 -2
  47. package/app/components/documents/DocumentSyncState.vue +1 -1
  48. package/app/components/documents/DocumentTemplatesModal.vue +5 -4
  49. package/app/components/documents/RepoContextDocPicker.vue +3 -6
  50. package/app/components/documents/StartFromDesignModal.vue +4 -4
  51. package/app/components/documents/TaskContextDocs.vue +1 -1
  52. package/app/components/environments/EnvSetupStepper.vue +3 -3
  53. package/app/components/environments/EnvironmentStatusPanel.vue +11 -10
  54. package/app/components/environments/steps/EnvPreflightStep.vue +8 -8
  55. package/app/components/environments/steps/EnvReviewStep.vue +23 -22
  56. package/app/components/environments/steps/EnvSaveStep.vue +5 -5
  57. package/app/components/focus/BlockFocusView.vue +11 -10
  58. package/app/components/followUp/FollowUpWindow.vue +10 -10
  59. package/app/components/forkDecision/ForkDecisionWindow.vue +23 -26
  60. package/app/components/foundational/FoundationalContractSummary.vue +2 -2
  61. package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -3
  62. package/app/components/foundational/FoundationalServiceRegistry.vue +3 -3
  63. package/app/components/foundational/FoundationalSuppressions.vue +2 -2
  64. package/app/components/fragments/FragmentLibraryManager.vue +3 -3
  65. package/app/components/fragments/FragmentSelector.vue +10 -11
  66. package/app/components/gates/GateFailingCheckList.vue +3 -3
  67. package/app/components/gates/GateResultView.vue +39 -38
  68. package/app/components/github/AddServiceFromRepoModal.vue +5 -4
  69. package/app/components/github/BranchProtectionPreflight.vue +5 -5
  70. package/app/components/github/GitHubConnect.vue +5 -3
  71. package/app/components/github/GitHubPanel.vue +14 -13
  72. package/app/components/github/RepoTreeBrowser.vue +1 -1
  73. package/app/components/humanTest/HumanTestWindow.vue +18 -17
  74. package/app/components/initiative/InitiativePlanNotice.vue +3 -3
  75. package/app/components/initiative/InitiativePlanReview.vue +19 -21
  76. package/app/components/initiative/InitiativePlanningWindow.vue +5 -5
  77. package/app/components/initiative/InitiativeTrackerWindow.vue +54 -53
  78. package/app/components/judge/JudgeResultView.vue +24 -25
  79. package/app/components/kaizen/KaizenPanel.vue +4 -4
  80. package/app/components/kaizen/KaizenStepStatus.vue +7 -6
  81. package/app/components/layout/AccountAuditLog.vue +1 -1
  82. package/app/components/layout/AccountDeploymentSettings.vue +10 -10
  83. package/app/components/layout/AccountFailureKindRules.vue +13 -13
  84. package/app/components/layout/AccountFoundationalSettings.vue +1 -1
  85. package/app/components/layout/AccountFragmentSettings.vue +1 -1
  86. package/app/components/layout/AccountModelPolicySettings.vue +7 -7
  87. package/app/components/layout/AccountPlatformAlertSettings.vue +8 -8
  88. package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
  89. package/app/components/layout/AccountRunCredentialSettings.vue +3 -3
  90. package/app/components/layout/AccountSkillSettings.vue +1 -1
  91. package/app/components/layout/AccountTeamSettings.vue +4 -0
  92. package/app/components/layout/AiProvidersBanner.vue +1 -1
  93. package/app/components/layout/AppearanceSwitcher.vue +1 -1
  94. package/app/components/layout/BoardSwitcher.vue +3 -2
  95. package/app/components/layout/BoardToolbar.vue +6 -2
  96. package/app/components/layout/CommandBar.vue +4 -2
  97. package/app/components/layout/DefaultTestEnvBanner.vue +2 -2
  98. package/app/components/layout/IntegrationsHub.vue +4 -3
  99. package/app/components/layout/LanguageSwitcher.vue +1 -1
  100. package/app/components/layout/ModelProvidersHub.vue +6 -4
  101. package/app/components/layout/NotificationsInbox.vue +8 -7
  102. package/app/components/layout/PersonalSetupModal.vue +5 -3
  103. package/app/components/layout/RolePrompt.vue +1 -1
  104. package/app/components/layout/SideBar.vue +3 -5
  105. package/app/components/layout/SpendWarningBanner.vue +3 -3
  106. package/app/components/layout/TranslationWarningBanner.vue +1 -1
  107. package/app/components/layout/UiModeSwitcher.vue +7 -6
  108. package/app/components/layout/UiRoleSwitcher.vue +2 -2
  109. package/app/components/media/ArtifactLightbox.vue +6 -6
  110. package/app/components/media/ImageCompare.vue +16 -16
  111. package/app/components/merge/MergeEffortChips.vue +4 -6
  112. package/app/components/notifications/NotificationSettingsPanel.vue +6 -5
  113. package/app/components/observability/OutcomeFilterChips.vue +1 -1
  114. package/app/components/observability/RunFailureSummary.vue +14 -17
  115. package/app/components/observability/StepMetricsBar.vue +9 -8
  116. package/app/components/observability/StepModelActivity.vue +4 -3
  117. package/app/components/observability/ToolCallList.vue +15 -18
  118. package/app/components/outcome/OutcomeSummaryWindow.vue +43 -42
  119. package/app/components/palettes/AgentPalette.vue +7 -5
  120. package/app/components/palettes/AgentTierSelect.vue +1 -1
  121. package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
  122. package/app/components/panels/AgentStepDetail.vue +27 -30
  123. package/app/components/panels/AttemptEntryHeader.vue +2 -2
  124. package/app/components/panels/DecisionModal.vue +1 -1
  125. package/app/components/panels/GenericStructuredResultView.vue +6 -5
  126. package/app/components/panels/InspectorPanel.vue +15 -13
  127. package/app/components/panels/MergerResultView.vue +10 -9
  128. package/app/components/panels/ObservabilityPanel.vue +66 -76
  129. package/app/components/panels/OperatorDashboardPanel.vue +27 -26
  130. package/app/components/panels/ReportsPanel.vue +26 -25
  131. package/app/components/panels/ReportsSpendBreakdown.vue +2 -2
  132. package/app/components/panels/ResultWindowShell.vue +19 -18
  133. package/app/components/panels/RunDetailLoadState.vue +1 -1
  134. package/app/components/panels/StepContainerStatus.vue +8 -7
  135. package/app/components/panels/StepDelegatedStatus.vue +13 -12
  136. package/app/components/panels/StepEffortReport.vue +12 -17
  137. package/app/components/panels/StepFragmentAdherence.vue +9 -10
  138. package/app/components/panels/StepMetadataCard.vue +41 -43
  139. package/app/components/panels/StepReproductionReport.vue +18 -20
  140. package/app/components/panels/StepRunMeta.vue +27 -26
  141. package/app/components/panels/StepTestReport.vue +16 -15
  142. package/app/components/panels/StepToolServers.vue +7 -8
  143. package/app/components/panels/StepValidationReport.vue +5 -5
  144. package/app/components/panels/inspector/ContainerSummary.vue +4 -4
  145. package/app/components/panels/inspector/DocReferenceRepos.vue +5 -4
  146. package/app/components/panels/inspector/EnvProbeReport.vue +16 -13
  147. package/app/components/panels/inspector/EpicChildren.vue +8 -7
  148. package/app/components/panels/inspector/FrontendBindingsResolved.vue +5 -4
  149. package/app/components/panels/inspector/FrontendConfig.vue +26 -34
  150. package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
  151. package/app/components/panels/inspector/InspectorSection.vue +7 -4
  152. package/app/components/panels/inspector/RecurringScheduleSettings.vue +9 -8
  153. package/app/components/panels/inspector/ServiceConnections.vue +2 -2
  154. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
  155. package/app/components/panels/inspector/ServiceSelfTests.vue +9 -13
  156. package/app/components/panels/inspector/ServiceTestConfig.vue +41 -45
  157. package/app/components/panels/inspector/ServiceTestSecrets.vue +3 -3
  158. package/app/components/panels/inspector/ServiceTestingContext.vue +2 -2
  159. package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
  160. package/app/components/panels/inspector/TaskAgentConfig.vue +2 -2
  161. package/app/components/panels/inspector/TaskAprioriBranches.vue +6 -5
  162. package/app/components/panels/inspector/TaskDependencies.vue +1 -1
  163. package/app/components/panels/inspector/TaskEstimateBadge.vue +3 -7
  164. package/app/components/panels/inspector/TaskExecution.vue +13 -12
  165. package/app/components/panels/inspector/TaskRunSettings.vue +35 -38
  166. package/app/components/panels/inspector/TaskStructure.vue +5 -4
  167. package/app/components/pipeline/AgentPromptEditor.vue +15 -14
  168. package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -52
  169. package/app/components/pipeline/EstimateThresholdFields.vue +2 -2
  170. package/app/components/pipeline/GateConfigFields.vue +6 -6
  171. package/app/components/pipeline/IterationCapPrompt.vue +1 -1
  172. package/app/components/pipeline/PipelineBuilder.vue +28 -27
  173. package/app/components/pipeline/PipelineHealthModal.vue +7 -7
  174. package/app/components/pipeline/PipelinePicker.vue +3 -3
  175. package/app/components/pipeline/PipelinePreview.vue +6 -7
  176. package/app/components/pipeline/PipelineProgress.vue +23 -24
  177. package/app/components/prReview/PrReviewWindow.vue +42 -41
  178. package/app/components/providers/AiPresetMismatchDialog.vue +4 -3
  179. package/app/components/providers/AiProviderOnboardingModal.vue +2 -2
  180. package/app/components/providers/ApiKeysSection.vue +7 -6
  181. package/app/components/providers/PersonalSubscriptionSection.vue +4 -3
  182. package/app/components/providers/VendorCredentialsModal.vue +5 -4
  183. package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -9
  184. package/app/components/ralph/RalphLoopResultView.vue +25 -26
  185. package/app/components/requirements/RequirementsReviewWindow.vue +13 -12
  186. package/app/components/riskPolicy/RiskPolicyPicker.vue +5 -5
  187. package/app/components/riskPolicy/RiskPolicyPreview.vue +16 -15
  188. package/app/components/sandbox/SandboxPanel.vue +21 -20
  189. package/app/components/settings/ApiTokensPanel.vue +1 -1
  190. package/app/components/settings/BudgetSettings.vue +18 -17
  191. package/app/components/settings/CapabilityCredentialsPanel.vue +5 -5
  192. package/app/components/settings/CloudflareHandlerSection.vue +16 -16
  193. package/app/components/settings/ComposeEnvironmentSetupSection.vue +14 -12
  194. package/app/components/settings/ConnectionTestVerdict.vue +1 -1
  195. package/app/components/settings/ConsensusGroupsSection.vue +21 -20
  196. package/app/components/settings/CustomManifestTypeEditor.vue +8 -7
  197. package/app/components/settings/DefaultProvisionTypeSection.vue +5 -5
  198. package/app/components/settings/InfraHandlersConfigurator.vue +14 -15
  199. package/app/components/settings/InfrastructureBackendPicker.vue +2 -2
  200. package/app/components/settings/IssueTrackerPanel.vue +9 -15
  201. package/app/components/settings/KubernetesEngineForm.vue +9 -8
  202. package/app/components/settings/KubernetesEnvironmentForm.vue +4 -3
  203. package/app/components/settings/LocalContainerPoolSettings.vue +2 -2
  204. package/app/components/settings/LocalModelEndpointsPanel.vue +10 -9
  205. package/app/components/settings/MergeClassRulesEditor.vue +6 -5
  206. package/app/components/settings/MergeRolePolicyEditor.vue +13 -12
  207. package/app/components/settings/ModelConfigurationPanel.vue +8 -11
  208. package/app/components/settings/ModelPresetHealthModal.vue +3 -3
  209. package/app/components/settings/ObservabilityConnectionPanel.vue +1 -1
  210. package/app/components/settings/OpenRouterCatalogPanel.vue +6 -5
  211. package/app/components/settings/PackageRegistriesPanel.vue +4 -4
  212. package/app/components/settings/ProviderConnectionTab.vue +5 -4
  213. package/app/components/settings/ProviderManifestEditor.vue +9 -8
  214. package/app/components/settings/ProviderPreferenceEditor.vue +8 -7
  215. package/app/components/settings/RiskPolicyCreateForm.vue +40 -26
  216. package/app/components/settings/RiskPolicyEditorRow.vue +18 -17
  217. package/app/components/settings/RiskPolicyHealthModal.vue +3 -3
  218. package/app/components/settings/RiskPolicyPanel.vue +11 -13
  219. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +5 -4
  220. package/app/components/settings/SharedStacksPanel.vue +5 -5
  221. package/app/components/settings/TaskSourceCard.vue +2 -2
  222. package/app/components/settings/TaskTypeSuppressionsPanel.vue +4 -4
  223. package/app/components/settings/ToolServerChecklist.vue +22 -22
  224. package/app/components/settings/UsageSettings.vue +13 -13
  225. package/app/components/settings/UserSecretsSection.vue +4 -3
  226. package/app/components/settings/WorkspaceMetadataSettings.vue +6 -5
  227. package/app/components/settings/WorkspaceSettingsPanel.vue +72 -58
  228. package/app/components/skills/ReviewSkillQueue.vue +8 -7
  229. package/app/components/skills/SkillLibraryManager.vue +2 -2
  230. package/app/components/slack/SlackPanel.vue +8 -7
  231. package/app/components/spec/ServiceSpecWindow.vue +15 -16
  232. package/app/components/tasks/BugHuntModal.vue +1 -1
  233. package/app/components/tasks/ContextIssuePicker.vue +4 -6
  234. package/app/components/tasks/TaskContextIssues.vue +1 -1
  235. package/app/components/tasks/TaskImportModal.vue +3 -2
  236. package/app/components/tasks/TaskSourceConnectModal.vue +5 -5
  237. package/app/components/testing/TestReportWindow.vue +54 -53
  238. package/app/components/tutorial/TutorialNudge.vue +3 -2
  239. package/app/components/tutorial/TutorialOverlay.vue +3 -2
  240. package/app/components/vcs/GitLabConnect.vue +1 -1
  241. package/app/components/visualConfirm/VisualConfirmationWindow.vue +23 -25
  242. package/app/utils/theme/builtins.ts +7 -1
  243. package/package.json +1 -1
@@ -23,24 +23,24 @@ const ordered = computed(() => [...props.failures].reverse())
23
23
  class="rounded-md border border-default/80 bg-app-950/50 px-2.5 py-2"
24
24
  data-testid="agent-failure-history-entry"
25
25
  >
26
- <div class="flex items-center gap-1.5 text-[10px] text-dimmed">
26
+ <div class="flex items-center gap-1.5 text-3xs text-dimmed">
27
27
  <UIcon name="i-lucide-alert-triangle" class="h-3 w-3 shrink-0 text-app-error-400/70" />
28
28
  <time>{{ d(new Date(failure.occurredAt), 'long') }}</time>
29
29
  </div>
30
30
 
31
- <p class="mt-1 text-[11px] leading-snug text-toned" :title="failure.message">
31
+ <p class="mt-1 text-2xs leading-snug text-toned" :title="failure.message">
32
32
  {{ failure.message }}
33
33
  </p>
34
34
 
35
- <p v-if="failure.hint" class="mt-1 text-[10px] leading-snug text-dimmed">
35
+ <p v-if="failure.hint" class="mt-1 text-3xs leading-snug text-dimmed">
36
36
  {{ failure.hint }}
37
37
  </p>
38
38
 
39
39
  <FailureDetail
40
40
  :detail="failure.detail"
41
41
  :message="failure.message"
42
- summary-class="text-[10px] text-dimmed hover:text-toned"
43
- pre-class="bg-app-950/80 text-[10px] text-muted"
42
+ summary-class="text-3xs text-dimmed hover:text-toned"
43
+ pre-class="bg-app-950/80 text-3xs text-muted"
44
44
  />
45
45
  </li>
46
46
  </ol>
@@ -12,6 +12,7 @@ import { BUILTIN_TASK_SOURCE_KINDS } from '@cat-factory/contracts'
12
12
  import { apiErrorReason } from '~/composables/api/errors'
13
13
  import { pipelineAllowedForSchedule } from '~/utils/pipeline'
14
14
  import { appliesIntakePredicate } from '~/utils/intakePredicates'
15
+ import SectionLabel from '~/components/common/SectionLabel.vue'
15
16
 
16
17
  const ui = useUiStore()
17
18
  const board = useBoardStore()
@@ -453,7 +454,7 @@ async function add() {
453
454
  <USwitch v-model="onDemand" :disabled="onDemandLocked" size="sm" class="mt-0.5" />
454
455
  <div class="space-y-0.5">
455
456
  <p class="text-xs font-medium text-default">{{ t('board.recurring.onDemand') }}</p>
456
- <p class="text-[11px] text-dimmed">
457
+ <p class="text-2xs text-dimmed">
457
458
  {{
458
459
  onDemandLocked
459
460
  ? t('board.recurring.onDemandLockedHint')
@@ -466,10 +467,10 @@ async function add() {
466
467
  <RecurringRecurrenceEditor v-if="!onDemand" v-model="recurrence" />
467
468
 
468
469
  <div v-if="filesTicket" class="space-y-3 rounded-lg border border-default p-3">
469
- <p class="text-[11px] font-semibold uppercase tracking-wide text-muted">
470
+ <SectionLabel as="p">
470
471
  {{ t('board.recurring.issueTracker') }}
471
- </p>
472
- <p class="text-[11px] text-dimmed">
472
+ </SectionLabel>
473
+ <p class="text-2xs text-dimmed">
473
474
  {{ t('board.recurring.issueTrackerHint') }}
474
475
  </p>
475
476
  <div class="flex gap-1">
@@ -541,22 +542,22 @@ async function add() {
541
542
  <p class="text-xs font-medium text-default">
542
543
  {{ t('board.recurring.trackerTrigger') }}
543
544
  </p>
544
- <p class="text-[11px] text-dimmed">
545
+ <p class="text-2xs text-dimmed">
545
546
  {{ t('board.recurring.trackerTriggerHint') }}
546
547
  </p>
547
548
  </div>
548
549
  </div>
549
550
 
550
551
  <div v-if="showIntake" class="space-y-3 rounded-lg border border-default p-3">
551
- <p class="text-[11px] font-semibold uppercase tracking-wide text-muted">
552
+ <SectionLabel as="p">
552
553
  {{ t('board.recurring.intake') }}
553
- </p>
554
- <p class="text-[11px] text-dimmed">
554
+ </SectionLabel>
555
+ <p class="text-2xs text-dimmed">
555
556
  {{ t('board.recurring.intakeHint') }}
556
557
  </p>
557
558
  <!-- Two different remedies: connect something, versus connect something ELSE. A source
558
559
  that is connected but cannot run a scheduled search is not an absent connection. -->
559
- <p v-if="intakeSources.length === 0" class="text-[11px] text-app-warning-500">
560
+ <p v-if="intakeSources.length === 0" class="text-2xs text-app-warning-500">
560
561
  {{
561
562
  tasks.anyOffered
562
563
  ? t('board.recurring.intakeNoIntakeSources')
@@ -626,7 +627,7 @@ async function add() {
626
627
  </UFormField>
627
628
 
628
629
  <template v-if="intakeSource">
629
- <p class="text-[11px] text-dimmed">
630
+ <p class="text-2xs text-dimmed">
630
631
  {{
631
632
  intakeDispatch === 'per-ticket'
632
633
  ? t('board.recurring.intakeDispatchPerTicketHint')
@@ -677,7 +678,7 @@ async function add() {
677
678
  </template>
678
679
  </div>
679
680
 
680
- <p class="text-[11px] text-dimmed">
681
+ <p class="text-2xs text-dimmed">
681
682
  {{ t('board.recurring.footerHint') }}
682
683
  </p>
683
684
  </div>
@@ -6,6 +6,7 @@
6
6
  // flag); the hard `blocked` tier does not. Driven by `ui.reviewFrictionContext`, mounted in
7
7
  // pages/index.vue. See backend/docs/review-debt-friction.md.
8
8
  import { computed } from 'vue'
9
+ import SectionLabel from '~/components/common/SectionLabel.vue'
9
10
 
10
11
  const { t } = useI18n()
11
12
  const ui = useUiStore()
@@ -84,9 +85,9 @@ function createAnyway() {
84
85
  <p class="text-sm text-toned">{{ body }}</p>
85
86
 
86
87
  <div class="rounded-lg border border-muted bg-default/50 p-2">
87
- <p class="mb-1.5 px-1 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
88
+ <SectionLabel as="p" class="mb-1.5 px-1">
88
89
  {{ t('errors.reviewFriction.waitingHeading') }}
89
- </p>
90
+ </SectionLabel>
90
91
  <ul class="space-y-1">
91
92
  <li v-for="item in ctx.debt" :key="item.blockId">
92
93
  <button
@@ -98,7 +99,7 @@ function createAnyway() {
98
99
  <span class="truncate text-default">
99
100
  {{ item.title || t('errors.reviewFriction.untitled') }}
100
101
  </span>
101
- <span class="shrink-0 text-[12px] text-dimmed">
102
+ <span class="shrink-0 text-xs text-dimmed">
102
103
  {{ t('errors.reviewFriction.waiting', { minutes: item.waitingMinutes }) }}
103
104
  </span>
104
105
  </button>
@@ -55,7 +55,7 @@ const entries = computed<Entry[]>(() =>
55
55
  >
56
56
  <!-- a superseded SUCCESSFUL attempt: its output, collapsible + copyable -->
57
57
  <template v-if="entry.kind === 'success'">
58
- <div class="flex items-center gap-1.5 text-[10px] text-dimmed">
58
+ <div class="flex items-center gap-1.5 text-3xs text-dimmed">
59
59
  <UIcon name="i-lucide-check-circle-2" class="h-3 w-3 shrink-0 text-app-success-400/70" />
60
60
  <time>{{ d(new Date(entry.occurredAt), 'long') }}</time>
61
61
  <span class="text-app-success-400/80">{{ t('panels.stepDetail.attemptSucceeded') }}</span>
@@ -63,31 +63,31 @@ const entries = computed<Entry[]>(() =>
63
63
  <div class="relative mt-1">
64
64
  <CopyButton :text="entry.output.output" class="absolute end-1 top-1 z-10" />
65
65
  <pre
66
- class="max-h-40 overflow-auto whitespace-pre-wrap rounded bg-app-950/80 p-1.5 pe-9 text-[10px] leading-snug text-toned"
66
+ class="max-h-40 overflow-auto whitespace-pre-wrap rounded-sm bg-app-950/80 p-1.5 pe-9 text-3xs leading-snug text-toned"
67
67
  >{{ entry.output.output }}</pre>
68
68
  </div>
69
- <p v-if="entry.output.truncated" class="mt-1 text-[10px] text-dimmed">
69
+ <p v-if="entry.output.truncated" class="mt-1 text-3xs text-dimmed">
70
70
  {{ t('panels.stepDetail.outputTruncated') }}
71
71
  </p>
72
72
  </template>
73
73
 
74
74
  <!-- a FAILED attempt: mirrors FailureHistoryList's entry markup -->
75
75
  <template v-else>
76
- <div class="flex items-center gap-1.5 text-[10px] text-dimmed">
76
+ <div class="flex items-center gap-1.5 text-3xs text-dimmed">
77
77
  <UIcon name="i-lucide-alert-triangle" class="h-3 w-3 shrink-0 text-app-error-400/70" />
78
78
  <time>{{ d(new Date(entry.occurredAt), 'long') }}</time>
79
79
  </div>
80
- <p class="mt-1 text-[11px] leading-snug text-toned" :title="entry.failure.message">
80
+ <p class="mt-1 text-2xs leading-snug text-toned" :title="entry.failure.message">
81
81
  {{ entry.failure.message }}
82
82
  </p>
83
- <p v-if="entry.failure.hint" class="mt-1 text-[10px] leading-snug text-dimmed">
83
+ <p v-if="entry.failure.hint" class="mt-1 text-3xs leading-snug text-dimmed">
84
84
  {{ entry.failure.hint }}
85
85
  </p>
86
86
  <FailureDetail
87
87
  :detail="entry.failure.detail"
88
88
  :message="entry.failure.message"
89
- summary-class="text-[10px] text-dimmed hover:text-toned"
90
- pre-class="bg-app-950/80 text-[10px] text-muted"
89
+ summary-class="text-3xs text-dimmed hover:text-toned"
90
+ pre-class="bg-app-950/80 text-3xs text-muted"
91
91
  />
92
92
  </template>
93
93
  </li>
@@ -52,7 +52,7 @@ const dim = computed(() => (props.size === 'sm' ? 'h-7 w-7' : 'h-9 w-9'))
52
52
  />
53
53
  </span>
54
54
  </div>
55
- <span v-if="size !== 'sm'" class="text-[10px] leading-none text-toned">
55
+ <span v-if="size !== 'sm'" class="text-3xs leading-none text-toned">
56
56
  {{ archetype.label }}
57
57
  </span>
58
58
  </div>
@@ -13,7 +13,6 @@ import { useBlockDrag } from '~/composables/useBlockDrag'
13
13
  import { useFrameStacking } from '~/composables/useFrameStacking'
14
14
  import { useViewport } from '~/composables/useViewport'
15
15
  import { laneBodyHeightIn } from '~/utils/laneGeometry'
16
-
17
16
  // Vue Flow passes the node's `id` and `data` as props to custom node components.
18
17
  // Only frames are rendered as board nodes; their tasks live inside the card.
19
18
  const props = defineProps<{ id: string }>()
@@ -249,10 +248,10 @@ const ITEM_ICON: Record<string, string> = {
249
248
  }}</span>
250
249
  </div>
251
250
  <div class="px-3 py-3">
252
- <p class="text-[11px] leading-snug text-muted">
251
+ <p class="text-2xs leading-snug text-muted">
253
252
  {{ t('board.frame.accessDenied.hint') }}
254
253
  </p>
255
- <code class="mt-2 block truncate font-mono text-[11px] text-dimmed">{{ block.id }}</code>
254
+ <code class="mt-2 block truncate font-mono text-2xs text-dimmed">{{ block.id }}</code>
256
255
  </div>
257
256
  </div>
258
257
 
@@ -316,9 +315,9 @@ const ITEM_ICON: Record<string, string> = {
316
315
  }}
317
316
  </span>
318
317
  </div>
319
- <div class="mt-1.5 h-1 w-full overflow-hidden rounded bg-app-warning-900/40">
318
+ <div class="mt-1.5 h-1 w-full overflow-hidden rounded-sm bg-app-warning-900/40">
320
319
  <div
321
- class="h-full rounded bg-app-warning-400 transition-all"
320
+ class="h-full rounded-sm bg-app-warning-400 transition-all"
322
321
  :style="{ width: bootstrapPct + '%' }"
323
322
  />
324
323
  </div>
@@ -327,7 +326,7 @@ const ITEM_ICON: Record<string, string> = {
327
326
  <li
328
327
  v-for="(item, i) in bootstrapItems"
329
328
  :key="i"
330
- class="flex items-start gap-1.5 text-[11px]"
329
+ class="flex items-start gap-1.5 text-2xs"
331
330
  :class="
332
331
  item.status === 'completed'
333
332
  ? 'text-app-warning-200/60 line-through'
@@ -430,7 +429,7 @@ const ITEM_ICON: Record<string, string> = {
430
429
  {{ t('board.frame.shared') }}
431
430
  </UBadge>
432
431
  </div>
433
- <div class="text-[11px] text-muted">{{ typeMeta!.label }}</div>
432
+ <div class="text-2xs text-muted">{{ typeMeta!.label }}</div>
434
433
  </div>
435
434
  </div>
436
435
  <div class="flex items-center gap-1">
@@ -530,7 +529,7 @@ const ITEM_ICON: Record<string, string> = {
530
529
  stays is what the canvas can't show at a glance. -->
531
530
  <div
532
531
  v-if="modules.length || prTasks"
533
- class="flex items-center gap-2 text-[10px] uppercase tracking-wide text-dimmed"
532
+ class="flex items-center gap-2 text-3xs uppercase tracking-wide text-dimmed"
534
533
  >
535
534
  <span v-if="modules.length">{{
536
535
  t('board.frame.moduleCount', { count: modules.length }, modules.length)
@@ -564,7 +563,7 @@ const ITEM_ICON: Record<string, string> = {
564
563
  v-if="!hasTasks && access.canWriteBoard.value"
565
564
  type="button"
566
565
  data-testid="frame-add-task-empty"
567
- class="absolute inset-4 flex items-center justify-center gap-1 rounded-lg border border-dashed border-muted text-[11px] text-dimmed hover:border-app-500 hover:text-toned"
566
+ class="absolute inset-4 flex items-center justify-center gap-1 rounded-lg border border-dashed border-muted text-2xs text-dimmed hover:border-app-500 hover:text-toned"
568
567
  @click.stop="addTask"
569
568
  >
570
569
  <UIcon name="i-lucide-plus" class="h-3.5 w-3.5" /> {{ t('board.frame.addFirstTask') }}
@@ -36,10 +36,10 @@ const selected = computed(() => ui.selectedBlockId === props.id)
36
36
  >
37
37
  <div class="flex items-center gap-1.5">
38
38
  <UIcon name="i-lucide-layers" class="h-3.5 w-3.5 shrink-0 text-app-secondary-400" />
39
- <span class="text-[10px] font-semibold uppercase tracking-wide text-app-secondary-300">{{
39
+ <span class="text-3xs font-semibold uppercase tracking-wide text-app-secondary-300">{{
40
40
  t('board.epic.label')
41
41
  }}</span>
42
- <span class="ms-auto text-[10px] text-muted">{{ done }}/{{ total }}</span>
42
+ <span class="ms-auto text-3xs text-muted">{{ done }}/{{ total }}</span>
43
43
  </div>
44
44
  <div class="mt-1 truncate text-sm font-medium text-app-100" :title="block.title">
45
45
  {{ block.title }}
@@ -50,10 +50,10 @@ const selected = computed(() => ui.selectedBlockId === props.id)
50
50
  :style="{ width: total ? `${Math.round((done / total) * 100)}%` : '0%' }"
51
51
  />
52
52
  </div>
53
- <div v-if="active" class="mt-1 text-[10px] text-muted">
53
+ <div v-if="active" class="mt-1 text-3xs text-muted">
54
54
  {{ t('board.epic.activeCount', { count: active }) }}
55
55
  </div>
56
- <div v-else-if="total === 0" class="mt-1 text-[10px] text-dimmed">
56
+ <div v-else-if="total === 0" class="mt-1 text-3xs text-dimmed">
57
57
  {{ t('board.epic.noTasksYet') }}
58
58
  </div>
59
59
  </div>
@@ -83,12 +83,12 @@ const withheldNote = computed(() => {
83
83
  class="h-3.5 w-3.5 shrink-0"
84
84
  :style="{ color: LANE_META.done.color }"
85
85
  />
86
- <span class="text-[11px] font-semibold text-default">{{ t(LANE_META.done.labelKey) }}</span>
86
+ <span class="text-2xs font-semibold text-default">{{ t(LANE_META.done.labelKey) }}</span>
87
87
  <!-- The TOTAL, not what the caps admitted: "this service has finished 312 tasks" is the
88
88
  fact the lane carries, and counting only the visible cards would understate it by
89
89
  two orders of magnitude. -->
90
90
  <span
91
- class="shrink-0 rounded px-1 text-[10px] font-semibold tabular-nums"
91
+ class="shrink-0 rounded-sm px-1 text-3xs font-semibold tabular-nums"
92
92
  :style="{
93
93
  backgroundColor: tint(LANE_META.done.color),
94
94
  color: LANE_META.done.color,
@@ -96,7 +96,7 @@ const withheldNote = computed(() => {
96
96
  data-testid="lane-count-done"
97
97
  >{{ doneLane.total }}</span
98
98
  >
99
- <span v-if="withheldNote" class="ms-auto truncate text-[10px] text-dimmed">{{
99
+ <span v-if="withheldNote" class="ms-auto truncate text-3xs text-dimmed">{{
100
100
  withheldNote
101
101
  }}</span>
102
102
  </button>
@@ -116,10 +116,7 @@ const withheldNote = computed(() => {
116
116
  >
117
117
  <!-- A zero cap is a real setting ("count them, show none"), so the strip explains why it
118
118
  has nothing in it rather than looking broken. -->
119
- <p
120
- v-if="doneSelection.shown.length === 0"
121
- class="px-1 text-[10px] leading-snug text-app-600"
122
- >
119
+ <p v-if="doneSelection.shown.length === 0" class="px-1 text-3xs leading-snug text-app-600">
123
120
  {{ t('board.lanes.done.allWithheld') }}
124
121
  </p>
125
122
  <LaneGroup
@@ -82,17 +82,17 @@ function select() {
82
82
  {{ statusLabel }}
83
83
  </UBadge>
84
84
  </div>
85
- <div class="mt-1 text-[10px] uppercase tracking-wide text-primary/70">
85
+ <div class="mt-1 text-3xs uppercase tracking-wide text-primary/70">
86
86
  {{ t('initiative.card.kind') }}
87
87
  </div>
88
88
  <div v-if="progress" class="mt-2 space-y-1">
89
- <div class="h-1.5 overflow-hidden rounded bg-elevated">
89
+ <div class="h-1.5 overflow-hidden rounded-sm bg-elevated">
90
90
  <div
91
- class="h-full rounded bg-primary"
91
+ class="h-full rounded-sm bg-primary"
92
92
  :style="{ width: `${Math.round((progress.settled / progress.total) * 100)}%` }"
93
93
  />
94
94
  </div>
95
- <div class="text-[10px] text-muted">
95
+ <div class="text-3xs text-muted">
96
96
  {{ t('initiative.card.progress', { done: progress.settled, total: progress.total }) }}
97
97
  </div>
98
98
  </div>
@@ -4,6 +4,7 @@ import { MODULE_META, taskTypeMeta } from '~/utils/catalog'
4
4
  import { LANE_GEOMETRY } from '~/utils/laneGeometry'
5
5
  import type { LaneGroup, LaneGroupKey } from '~/utils/laneSort'
6
6
  import { LANE_REASON_LABEL_KEYS, type LaneReason } from '~/utils/swimlanes'
7
+ import SectionLabel from '~/components/common/SectionLabel.vue'
7
8
 
8
9
  /**
9
10
  * One labelled run of cards inside a lane.
@@ -73,14 +74,11 @@ const icon = computed(() => {
73
74
  <div :data-drop-zone="dropZone ?? undefined" class="space-y-1.5">
74
75
  <!-- `none` grouping renders no header: one unlabelled group IS the flat lane, and a header
75
76
  saying "all of them" would be a row of chrome carrying no information. -->
76
- <div
77
- v-if="groupKey !== 'none'"
78
- class="flex items-center gap-1 px-0.5 text-[10px] uppercase tracking-wide text-dimmed"
79
- >
77
+ <SectionLabel v-if="groupKey !== 'none'" class="flex items-center gap-1 px-0.5">
80
78
  <UIcon v-if="icon" :name="icon" class="h-3 w-3 shrink-0" />
81
79
  <span class="truncate" :title="label">{{ label }}</span>
82
80
  <span class="ms-auto shrink-0 tabular-nums">{{ group.entries.length }}</span>
83
- </div>
81
+ </SectionLabel>
84
82
  <div :class="layout === 'grid' ? 'flex flex-wrap items-start gap-2' : 'space-y-1.5'">
85
83
  <LaneTask
86
84
  v-for="entry in group.entries"
@@ -327,7 +327,7 @@ function selectTask() {
327
327
  hover. Renders a built-in OR a deployment-registered custom type via `taskTypeMeta`. -->
328
328
  <span
329
329
  v-if="typeBadge"
330
- class="inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded"
330
+ class="inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-sm"
331
331
  :style="{ color: typeBadge.color, backgroundColor: tint(typeBadge.color) }"
332
332
  :title="typeBadge.label"
333
333
  :data-task-type-badge="task.taskType"
@@ -346,7 +346,7 @@ function selectTask() {
346
346
  "
347
347
  />
348
348
  <span
349
- class="ms-auto truncate text-[9px] uppercase tracking-wide"
349
+ class="ms-auto truncate text-3xs uppercase tracking-wide"
350
350
  :class="
351
351
  runFailed
352
352
  ? 'text-app-error-400'
@@ -379,7 +379,7 @@ function selectTask() {
379
379
  title is the one always-rendered part of the body that no control can occupy. -->
380
380
  <div
381
381
  data-testid="task-title"
382
- class="mt-1 line-clamp-2 break-words text-[11px] font-semibold leading-snug text-app-100"
382
+ class="mt-1 line-clamp-2 break-words text-2xs font-semibold leading-snug text-app-100"
383
383
  :title="task.title"
384
384
  >
385
385
  {{ task.title }}
@@ -415,7 +415,7 @@ function selectTask() {
415
415
  <span
416
416
  v-for="d in deps"
417
417
  :key="d.id"
418
- class="inline-flex items-center gap-0.5 rounded bg-elevated/80 px-1 py-0.5 text-[9px]"
418
+ class="inline-flex items-center gap-0.5 rounded-sm bg-elevated/80 px-1 py-0.5 text-3xs"
419
419
  :class="d.status === 'done' ? 'text-muted' : 'text-app-warning-300'"
420
420
  :title="depLabel(d)"
421
421
  >
@@ -431,7 +431,7 @@ function selectTask() {
431
431
  <div class="nodrag mt-2 flex flex-wrap items-center gap-1">
432
432
  <!-- a reviewer gate folding/re-reviewing in the background: a working indicator,
433
433
  NOT a gate — the human is back on the board and summoned only if input is needed -->
434
- <span v-if="reviewStage" class="inline-flex items-center gap-1 text-[9px] text-primary">
434
+ <span v-if="reviewStage" class="inline-flex items-center gap-1 text-3xs text-primary">
435
435
  <UIcon name="i-lucide-loader-circle" class="h-3 w-3 animate-spin" />
436
436
  {{ reviewStageLabel }}
437
437
  </span>
@@ -481,7 +481,7 @@ function selectTask() {
481
481
  </UButton>
482
482
  <span
483
483
  v-if="runnable && defaultPipeline"
484
- class="inline-flex items-center gap-0.5 text-[9px] text-dimmed"
484
+ class="inline-flex items-center gap-0.5 text-3xs text-dimmed"
485
485
  >
486
486
  <UIcon name="i-lucide-workflow" class="h-2.5 w-2.5" />{{ defaultPipeline.name }}
487
487
  </span>
@@ -539,7 +539,7 @@ function selectTask() {
539
539
  <!-- A merged task is the one people come back to READ, so its result stays openable
540
540
  rather than collapsing to a tick the moment it lands. -->
541
541
  <template v-else-if="task.status === 'done'">
542
- <span class="inline-flex items-center gap-1 text-[9px] text-app-success-400">
542
+ <span class="inline-flex items-center gap-1 text-3xs text-app-success-400">
543
543
  <UIcon name="i-lucide-check-check" class="h-3 w-3" /> {{ t('board.task.implemented') }}
544
544
  </span>
545
545
  <UButton
@@ -565,7 +565,7 @@ function selectTask() {
565
565
  class="mt-2 flex flex-wrap items-center gap-1 border-t border-default pt-2"
566
566
  >
567
567
  <span
568
- class="inline-flex items-center gap-1 rounded bg-app-secondary-500/15 px-1.5 py-0.5 text-[9px] text-app-secondary-200"
568
+ class="inline-flex items-center gap-1 rounded-sm bg-app-secondary-500/15 px-1.5 py-0.5 text-3xs text-app-secondary-200"
569
569
  :title="t('board.task.module', { name: task.moduleName })"
570
570
  >
571
571
  <UIcon :name="MODULE_META.icon" class="h-3 w-3" :style="{ color: MODULE_META.color }" />
@@ -46,9 +46,9 @@ const isEmpty = computed(() => props.rendered.groups.every((g) => g.entries.leng
46
46
  :style="{ borderColor: tint(meta.color, 20) }"
47
47
  >
48
48
  <UIcon :name="meta.icon" class="h-3.5 w-3.5 shrink-0" :style="{ color: meta.color }" />
49
- <span class="truncate text-[11px] font-semibold text-default">{{ t(meta.labelKey) }}</span>
49
+ <span class="truncate text-2xs font-semibold text-default">{{ t(meta.labelKey) }}</span>
50
50
  <span
51
- class="ms-auto shrink-0 rounded px-1 text-[10px] font-semibold tabular-nums"
51
+ class="ms-auto shrink-0 rounded-sm px-1 text-3xs font-semibold tabular-nums"
52
52
  :style="{ backgroundColor: tint(meta.color), color: meta.color }"
53
53
  :data-testid="`lane-count-${rendered.lane}`"
54
54
  >{{ rendered.total }}</span
@@ -65,7 +65,7 @@ const isEmpty = computed(() => props.rendered.groups.every((g) => g.entries.leng
65
65
  <!-- An empty lane SAYS it is empty. Left blank, "nothing needs you" and "the lane failed
66
66
  to render" look identical, and the first is worth stating: it is the answer a reader
67
67
  scanning the needs-you column is hoping for. -->
68
- <p v-if="isEmpty" class="px-1 pt-2 text-[10px] leading-snug text-app-600">
68
+ <p v-if="isEmpty" class="px-1 pt-2 text-3xs leading-snug text-app-600">
69
69
  {{ t(meta.emptyKey) }}
70
70
  </p>
71
71
  <LaneGroup
@@ -12,6 +12,7 @@ import {
12
12
  import { lodAtLeast } from '~/composables/useSemanticZoom'
13
13
  import { prReviewPhase } from '~/utils/prReviewProgress'
14
14
  import PrReviewPhaseBadge from '~/components/prReview/PrReviewPhaseBadge.vue'
15
+ import SectionLabel from '~/components/common/SectionLabel.vue'
15
16
 
16
17
  // Drill-down inside a task card: the task's build-pipeline steps appear on hover (at any
17
18
  // zoom level) or once the `steps` zoom band is reached, and one band deeper (`subtasks`)
@@ -101,14 +102,14 @@ const ITEM_ICON: Record<string, string> = {
101
102
 
102
103
  <template>
103
104
  <div v-if="showSteps" class="mt-2 space-y-1 border-t border-default pt-2">
104
- <div class="flex items-center gap-1 text-[9px] uppercase tracking-wide text-dimmed">
105
+ <SectionLabel class="flex items-center gap-1">
105
106
  <UIcon name="i-lucide-workflow" class="h-2.5 w-2.5" />
106
107
  {{ t('board.task.buildSteps') }}
107
- </div>
108
- <div v-for="(s, i) in steps" :key="i" class="rounded bg-default/60 px-1.5 py-1">
108
+ </SectionLabel>
109
+ <div v-for="(s, i) in steps" :key="i" class="rounded-sm bg-default/60 px-1.5 py-1">
109
110
  <button
110
111
  type="button"
111
- class="flex w-full cursor-pointer items-center gap-1 rounded text-start focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
112
+ class="flex w-full cursor-pointer items-center gap-1 rounded-sm text-start focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
112
113
  :title="`${agentKindMeta(s.agentKind).label} — ${agentKindMeta(s.agentKind).description}\n${t('board.task.clickToViewStep')}`"
113
114
  @click.stop="openStep(i)"
114
115
  >
@@ -117,7 +118,7 @@ const ITEM_ICON: Record<string, string> = {
117
118
  class="h-3 w-3 shrink-0"
118
119
  :style="{ color: agentKindMeta(s.agentKind).color }"
119
120
  />
120
- <span class="truncate text-[10px] text-default">
121
+ <span class="truncate text-3xs text-default">
121
122
  {{ agentKindMeta(s.agentKind).label }}
122
123
  </span>
123
124
  <UIcon
@@ -129,7 +130,7 @@ const ITEM_ICON: Record<string, string> = {
129
130
  live — its slice progress reads better as Slicing… / Reviewing X/Y slices. -->
130
131
  <span
131
132
  v-if="s.subtasks && s.subtasks.total > 0 && !prPhaseActive(s)"
132
- class="ms-auto shrink-0 font-mono text-[9px] tabular-nums text-muted"
133
+ class="ms-auto shrink-0 font-mono text-3xs tabular-nums text-muted"
133
134
  >
134
135
  {{ s.subtasks.completed }}/{{ s.subtasks.total }}
135
136
  </span>
@@ -149,7 +150,7 @@ const ITEM_ICON: Record<string, string> = {
149
150
  v-if="prPhaseActive(s)"
150
151
  :step="s"
151
152
  :run-failed="runFailed"
152
- class="ms-4 mt-1 text-[9px]"
153
+ class="ms-4 mt-1 text-3xs"
153
154
  />
154
155
 
155
156
  <!-- pending approval gate: jump straight to the conclusions reader. Suppressed
@@ -162,7 +163,7 @@ const ITEM_ICON: Record<string, string> = {
162
163
  !reviews.isBackground(s.agentKind, props.taskId)
163
164
  "
164
165
  type="button"
165
- class="mt-1 flex w-full items-center justify-center gap-1 rounded bg-app-warning-500 px-1.5 py-0.5 text-[9px] font-semibold text-app-warning-50 dark:text-app-warning-950 transition hover:bg-app-warning-400"
166
+ class="mt-1 flex w-full items-center justify-center gap-1 rounded-sm bg-app-warning-500 px-1.5 py-0.5 text-3xs font-semibold text-app-warning-50 dark:text-app-warning-950 transition hover:bg-app-warning-400"
166
167
  @click.stop="ui.openApprovalDetail(instance.id, s.approval.id)"
167
168
  >
168
169
  <UIcon name="i-lucide-shield-check" class="h-2.5 w-2.5" />
@@ -172,10 +173,10 @@ const ITEM_ICON: Record<string, string> = {
172
173
  <!-- per-step subtask progress bar -->
173
174
  <div
174
175
  v-if="s.subtasks && s.subtasks.total > 0"
175
- class="mt-1 h-0.5 w-full overflow-hidden rounded bg-accented/60"
176
+ class="mt-1 h-0.5 w-full overflow-hidden rounded-sm bg-accented/60"
176
177
  >
177
178
  <div
178
- class="h-full rounded bg-primary transition-all"
179
+ class="h-full rounded-sm bg-primary transition-all"
179
180
  :style="{ width: `${(s.subtasks.completed / s.subtasks.total) * 100}%` }"
180
181
  />
181
182
  </div>
@@ -183,7 +184,7 @@ const ITEM_ICON: Record<string, string> = {
183
184
  <!-- conditionally-run companion (the gate's ci-fixer / conflict-resolver, or the
184
185
  Tester's fixer): a compact running/ran/skipped line, so a gate that's working
185
186
  its helper reads as actively fixing rather than a frozen subtask list. -->
186
- <div v-if="companionByStep[i]" class="mt-1 flex items-center gap-1 text-[9px]">
187
+ <div v-if="companionByStep[i]" class="mt-1 flex items-center gap-1 text-3xs">
187
188
  <UIcon
188
189
  :name="agentKindMeta(companionByStep[i]!.kind).icon"
189
190
  class="h-2.5 w-2.5 shrink-0"
@@ -208,7 +209,7 @@ const ITEM_ICON: Record<string, string> = {
208
209
  <li
209
210
  v-for="(item, j) in s.subtasks.items"
210
211
  :key="j"
211
- class="flex items-start gap-1 text-[9px]"
212
+ class="flex items-start gap-1 text-3xs"
212
213
  :class="
213
214
  item.status === 'completed'
214
215
  ? 'text-dimmed line-through'
@@ -9,6 +9,7 @@
9
9
  // is no suggestion at all as its own state, because "the two repositories agreed on everything"
10
10
  // and "the analysis never ran" would otherwise look identical and lead to opposite conclusions.
11
11
  import type { AdoptionSource, BootstrapJob } from '~/types/domain'
12
+ import SectionLabel from '~/components/common/SectionLabel.vue'
12
13
 
13
14
  const props = defineProps<{ job: BootstrapJob }>()
14
15
  const emit = defineEmits<{ close: [] }>()
@@ -239,7 +240,7 @@ watch(open, (isOpen) => {
239
240
  class="flex items-baseline gap-2"
240
241
  >
241
242
  <span
242
- class="shrink-0 font-mono text-[10px] uppercase"
243
+ class="shrink-0 font-mono text-3xs uppercase"
243
244
  :class="entry.outcome === 'read' ? 'text-dimmed' : 'text-app-warning-400/80'"
244
245
  >
245
246
  {{ t(`bootstrap.adoption.survey.outcome.${entry.outcome}`) }}
@@ -289,9 +290,9 @@ watch(open, (isOpen) => {
289
290
  <div class="flex items-start justify-between gap-3">
290
291
  <div>
291
292
  <p class="text-sm font-medium text-app-100">{{ decision.title }}</p>
292
- <p class="text-[11px] uppercase tracking-wide text-dimmed">
293
+ <SectionLabel as="p">
293
294
  {{ t(`bootstrap.adoption.area.${decision.area}`) }}
294
- </p>
295
+ </SectionLabel>
295
296
  </div>
296
297
  <UBadge v-if="!touched.has(decision.id)" color="warning" variant="subtle" size="sm">
297
298
  {{ t('bootstrap.adoption.needsYou') }}
@@ -299,7 +300,7 @@ watch(open, (isOpen) => {
299
300
  </div>
300
301
 
301
302
  <div class="grid gap-2 text-xs sm:grid-cols-2">
302
- <div class="rounded bg-default/60 p-2">
303
+ <div class="rounded-sm bg-default/60 p-2">
303
304
  <p class="mb-1 font-semibold text-toned">
304
305
  {{ t('bootstrap.adoption.side.monorepo') }}
305
306
  </p>
@@ -307,7 +308,7 @@ watch(open, (isOpen) => {
307
308
  {{ decision.monorepoPractice ?? t('bootstrap.adoption.side.nothing') }}
308
309
  </p>
309
310
  </div>
310
- <div class="rounded bg-default/60 p-2">
311
+ <div class="rounded-sm bg-default/60 p-2">
311
312
  <p class="mb-1 font-semibold text-toned">
312
313
  {{ t('bootstrap.adoption.side.template') }}
313
314
  </p>
@@ -321,7 +322,7 @@ watch(open, (isOpen) => {
321
322
 
322
323
  <!-- The evidence is what makes the suggestion checkable rather than an assertion,
323
324
  so it is shown, not tucked away. -->
324
- <p v-if="decision.evidence.length" class="text-[11px] text-dimmed">
325
+ <p v-if="decision.evidence.length" class="text-2xs text-dimmed">
325
326
  {{ t('bootstrap.adoption.evidence') }}
326
327
  <span class="font-mono">{{ decision.evidence.join(', ') }}</span>
327
328
  </p>