From a255131e66318bed23018f3d6798ebb1ea33676a Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 25 Jun 2026 13:50:57 +0800
Subject: [PATCH] Freeze 8.0.2 usage tips banner

---
 src/Apptag/Localization/fr.json                                   |    5 
 src/Apptag/Localization/ru.json                                   |    5 
 src/Apptag/AppGridCollectionView.swift                            |  417 +++++++++++++++++---
 src/Scripts/usage_tips_qa.sh                                      |  539 +++++++++++---------------
 src/Apptag/Localization/ar.json                                   |    5 
 src/Apptag/Localization/ro.json                                   |    5 
 src/Apptag/Localization/en.json                                   |    5 
 src/Apptag/Localization/sv.json                                   |    5 
 src/Apptag/Localization/zh-Hant.json                              |    5 
 src/Docs/Requirements/2026-06-24-app-grid-theme-system-todo.md    |    6 
 src/Apptag/Localization/pl.json                                   |    5 
 src/Apptag/Localization/es.json                                   |    5 
 src/Apptag/Localization/th.json                                   |    5 
 src/Apptag/Localization/nn.json                                   |    5 
 src/CHANGELOG.md                                                  |   11 
 src/Apptag/Info.plist                                             |    4 
 src/Docs/Requirements/2026-06-24-app-grid-theme-system-worklog.md |   61 +++
 src/Apptag/Localization/cs.json                                   |    5 
 src/Apptag/Localization/ms.json                                   |    5 
 src/Apptag/Localization/it.json                                   |    5 
 src/Apptag/Localization/tr.json                                   |    5 
 src/Apptag/Localization/da.json                                   |    5 
 src/Apptag/Localization/id.json                                   |    5 
 src/Apptag/Localization/ja.json                                   |    5 
 src/Apptag/Localization/de.json                                   |    5 
 src/Apptag/Localization/pt-BR.json                                |    5 
 CODEGRAPH.md                                                      |    8 
 src/Apptag/Localization/ar-Najdi.json                             |    5 
 src/Apptag/Localization/nb.json                                   |    5 
 src/Apptag/Localization/ko.json                                   |    5 
 src/Apptag/Localization/no.json                                   |    5 
 src/Apptag/ContentView.swift                                      |    1 
 src/Apptag/Localization/sr-Cyrl.json                              |    5 
 src/Apptag/Localization/zh-Hans.json                              |    5 
 src/Apptag/Localization/nl.json                                   |    5 
 src/Apptag/Localization/uk.json                                   |    5 
 src/Apptag/Localization/vi.json                                   |    5 
 37 files changed, 741 insertions(+), 451 deletions(-)

diff --git a/CODEGRAPH.md b/CODEGRAPH.md
index d4f8e62..51602b4 100644
--- a/CODEGRAPH.md
+++ b/CODEGRAPH.md
@@ -13,7 +13,7 @@
   - Keeps the most recent in-process `AppLibrarySnapshot` so newly-created overlay views can render immediately while background refresh catches up.
 
 - AppGrid collection renderer: `src/Apptag/AppGridCollectionView.swift`
-  - AppKit `NSCollectionView` implementation for grouped app layout, drag/drop, app ordering, bubbles, and usage tips overlay.
+  - AppKit `NSCollectionView` implementation for grouped app layout, drag/drop, app ordering, bubbles, and the native split teaching-banner usage tips overlay.
   - Should not own full-screen AppGrid theme gradients; it receives theme only for derived glass readability.
 
 - AppGrid theme model: `src/Apptag/AppGridTheme.swift`
@@ -44,7 +44,7 @@
 - `src/Scripts/macos14_build_metadata_qa.sh`
   - Verifies build metadata and deployment target.
 - `src/Scripts/usage_tips_qa.sh`
-  - Verifies usage tips overlay and 29-language coverage.
+  - Verifies native usage tips overlay, split teaching-banner layout, theme-aware accent/readability, click-through protection, close action, ordered-list details, and 29-language coverage.
 - `src/Scripts/tag_navigation_hover_scroll_qa.sh`
   - Verifies tag hover scroll semantics remain guarded.
 - `src/Scripts/tag_double_click_preferences_qa.sh`
@@ -61,5 +61,7 @@
 - The default theme preserves the original light AppGrid background.
 - Legacy users with `useDarkAppGrid=true` must land on the `deepBlue` theme.
 - Tag navigation single-click must keep immediate scroll behavior. Hover must keep guarded auto-scroll. Long-press must keep tag reorder behavior. Double-click may open Preferences on the Tags tab but must not replace those existing behaviors.
+- Usage tips must remain a native AppKit overlay, not SwiftUI. The bottom teaching banner must consume its own click region so it never opens underlying apps; only arrow hit regions change pages.
+- Usage tips title/detail text must remain readable across the default, dark, and bright App Grid themes; all visible usage-tip copy must stay complete across 29 localization files.
 
-Last updated: 2026-06-24, tag double-click opens Tags settings.
+Last updated: 2026-06-25, usage tips teaching banner visual optimization.
diff --git a/src/Apptag/AppGridCollectionView.swift b/src/Apptag/AppGridCollectionView.swift
index 5d8d845..13fe190 100644
--- a/src/Apptag/AppGridCollectionView.swift
+++ b/src/Apptag/AppGridCollectionView.swift
@@ -38,8 +38,8 @@
 }
 
 enum AppGridUsageTipsMetrics {
-    static let barHeight: CGFloat = 136
-    static let reservedHeight: CGFloat = 176
+    static let barHeight: CGFloat = 154
+    static let reservedHeight: CGFloat = 194
     static let bottomMargin: CGFloat = 20
     static let horizontalInset: CGFloat = 24
     static let minWidth: CGFloat = 640
@@ -71,6 +71,7 @@
     let onGroupActivate: (String) -> Void
     let onScrollActivity: () -> Void
     let onDragModeChange: (Bool) -> Void
+    let onHideUsageTips: () -> Void
     let onUsageTipsHoverChange: (Bool) -> Void
 
     func makeCoordinator() -> Coordinator {
@@ -111,6 +112,7 @@
             onGroupActivate: onGroupActivate,
             onScrollActivity: onScrollActivity,
             onDragModeChange: onDragModeChange,
+            onHideUsageTips: onHideUsageTips,
             onUsageTipIndexChange: { selectedUsageTipIndexBinding.wrappedValue = $0 },
             onUsageTipsHoverChange: onUsageTipsHoverChange
         )
@@ -149,6 +151,7 @@
         var onGroupActivate: (String) -> Void = { _ in }
         var onScrollActivity: () -> Void = {}
         var onDragModeChange: (Bool) -> Void = { _ in }
+        var onHideUsageTips: () -> Void = {}
         var onUsageTipIndexChange: (Int) -> Void = { _ in }
         var onUsageTipsHoverChange: (Bool) -> Void = { _ in }
 
@@ -184,6 +187,7 @@
             onGroupActivate: @escaping (String) -> Void,
             onScrollActivity: @escaping () -> Void,
             onDragModeChange: @escaping (Bool) -> Void,
+            onHideUsageTips: @escaping () -> Void,
             onUsageTipIndexChange: @escaping (Int) -> Void,
             onUsageTipsHoverChange: @escaping (Bool) -> Void
         ) {
@@ -212,6 +216,7 @@
             self.onGroupActivate = onGroupActivate
             self.onScrollActivity = onScrollActivity
             self.onDragModeChange = onDragModeChange
+            self.onHideUsageTips = onHideUsageTips
             self.onUsageTipIndexChange = onUsageTipIndexChange
             self.onUsageTipsHoverChange = onUsageTipsHoverChange
             if !self.usageTipsVisible {
@@ -838,10 +843,12 @@
     weak var coordinator: AppGridCollectionView.Coordinator?
 
     private let backgroundView = NSVisualEffectView()
+    private let titlePanelView = NSView()
     private let iconView = AppGridDecorativeImageView()
     private let titleLabel = NSTextField(labelWithString: "")
     private let detailScrollView = NSScrollView()
     private let detailLabel = NSTextField(labelWithString: "")
+    private let closeButton = AppGridUsageTipIconButton(systemImage: "xmark")
     private let previousButton = AppGridUsageTipIconButton(systemImage: "chevron.left")
     private let nextButton = AppGridUsageTipIconButton(systemImage: "chevron.right")
     private let dotsView = AppGridUsageTipDotsView()
@@ -850,9 +857,10 @@
     private var visualWidth: CGFloat = AppGridUsageTipsMetrics.minWidth
     private var visualHeight: CGFloat = AppGridUsageTipsMetrics.barHeight
     private var visualBottomMargin: CGFloat = AppGridUsageTipsMetrics.bottomMargin
+    private var currentDetailText = ""
 
-    private let titleFont = NSFont.systemFont(ofSize: 24, weight: .semibold)
-    private let detailFont = NSFont.systemFont(ofSize: 24, weight: .regular)
+    private let titleFont = NSFont.systemFont(ofSize: 25, weight: .bold)
+    private let detailFont = NSFont.systemFont(ofSize: 24, weight: .medium)
 
     override var isFlipped: Bool { true }
     override var acceptsFirstResponder: Bool { true }
@@ -954,41 +962,65 @@
         let visualFrame = currentVisualFrame()
         backgroundView.frame = visualFrame
 
-        let paddingLeft: CGFloat = 22
-        let paddingRight: CGFloat = 16
-        let iconSize: CGFloat = 24
-        let iconTextGap: CGFloat = 10
-        let controlZoneWidth: CGFloat = 176
-        let buttonGap: CGFloat = 6
-        let buttonSize: CGFloat = 48
-        let buttonDotsGap: CGFloat = 6
+        let outerPaddingX: CGFloat = 20
+        let outerPaddingY: CGFloat = 19
+        let titlePanelWidth = min(
+            max(350, visualFrame.width * 0.24),
+            min(460, visualFrame.width * 0.36)
+        )
+        let titlePanelHeight = max(1, min(116, visualFrame.height - outerPaddingY * 2))
+        let titlePanelFrame = NSRect(
+            x: visualFrame.minX + outerPaddingX,
+            y: visualFrame.midY - titlePanelHeight / 2,
+            width: titlePanelWidth,
+            height: titlePanelHeight
+        )
+        titlePanelView.frame = titlePanelFrame
+
+        let iconSize: CGFloat = 26
+        let controlZoneWidth: CGFloat = min(148, max(128, visualFrame.width * 0.08))
+        let buttonGap: CGFloat = 12
+        let buttonSize: CGFloat = 52
+        let buttonDotsGap: CGFloat = 10
         let dotsHeight: CGFloat = 10
         let dotsWidth = dotsView.preferredWidth
 
-        let titleX = visualFrame.minX + paddingLeft + iconSize + iconTextGap
+        let titleIconGap: CGFloat = 20
+        let titleSidePadding: CGFloat = 28
+        let titleX = titlePanelFrame.minX + titleSidePadding + iconSize + titleIconGap
+        let titleWidth = max(1, titlePanelFrame.maxX - titleX - titleSidePadding)
+        let titleHeight: CGFloat = 68
+        let titleY = titlePanelFrame.midY - titleHeight / 2
+        let detailGap = visualFrame.width < 980 ? CGFloat(30) : CGFloat(52)
+        let controlGap = visualFrame.width < 980 ? CGFloat(24) : CGFloat(36)
+        let detailX = titlePanelFrame.maxX + detailGap
+        let controlsMaxX = visualFrame.maxX - 54
+        let textRight = controlsMaxX - controlZoneWidth - controlGap
+        let detailHeight: CGFloat = 88
+        let detailY = visualFrame.minY + max(0, (visualFrame.height - detailHeight) / 2)
+
         let buttonsWidth = buttonSize * 2 + buttonGap
-        let buttonsGroupMinX = visualFrame.maxX - paddingRight - buttonsWidth
-        let textRight = visualFrame.maxX - controlZoneWidth
-        let availableTextWidth = max(1, textRight - titleX)
-        let titleWidth = availableTextWidth
-        let titleHeight: CGFloat = 32
-        let detailHeight: CGFloat = 68
-        let lineGap: CGFloat = 8
-        let textBlockHeight = titleHeight + lineGap + detailHeight
-        let textBlockY = visualFrame.minY + max(0, (visualFrame.height - textBlockHeight) / 2)
+        let buttonsGroupMinX = controlsMaxX - buttonsWidth
 
         iconView.frame = NSRect(
-            x: visualFrame.minX + paddingLeft,
-            y: visualFrame.midY - iconSize / 2,
+            x: titlePanelFrame.minX + titleSidePadding,
+            y: titlePanelFrame.midY - iconSize / 2,
             width: iconSize,
             height: iconSize
         )
 
         titleLabel.frame = NSRect(
             x: titleX,
-            y: textBlockY,
+            y: titleY,
             width: titleWidth,
             height: titleHeight
+        )
+
+        closeButton.frame = NSRect(
+            x: visualFrame.maxX - 40,
+            y: visualFrame.minY + 10,
+            width: 30,
+            height: 30
         )
 
         let controlGroupHeight = buttonSize + buttonDotsGap + dotsHeight
@@ -1012,12 +1044,10 @@
             height: dotsHeight
         )
 
-        let detailX = titleX
-        let detailRight = textRight
         detailScrollView.frame = NSRect(
             x: detailX,
-            y: titleLabel.frame.maxY + lineGap,
-            width: max(1, detailRight - detailX),
+            y: detailY,
+            width: max(1, textRight - detailX),
             height: detailHeight
         )
         layoutDetailLabel()
@@ -1037,7 +1067,9 @@
         let safeIndex = min(max(0, coordinator.selectedUsageTipIndex), coordinator.usageTips.count - 1)
         let tip = coordinator.usageTips[safeIndex]
         titleLabel.stringValue = tr(tip.titleKey)
-        detailLabel.stringValue = formattedTipDetail(tr(tip.detailKey))
+        updateTipIcon(id: tip.id)
+        currentDetailText = formattedTipDetail(tr(tip.detailKey))
+        closeButton.buttonAccessibilityLabel = tr("usageTips.close")
         previousButton.buttonAccessibilityLabel = tr("usageTips.previous")
         nextButton.buttonAccessibilityLabel = tr("usageTips.next")
         dotsView.configure(count: coordinator.usageTips.count, selectedIndex: safeIndex)
@@ -1059,13 +1091,7 @@
     }
 
     func preferredWidth(maxAvailableWidth: CGFloat) -> CGFloat {
-        let padding: CGFloat = 22 + 16
-        let fixedWidth: CGFloat = 24 + 10 + 24 + 48 + 6 + 48
-        let titleWidth = ceil(titleLabel.attributedStringValue.size().width)
-        let detailWidth = measuredDetailLineWidth()
-        let contentWidth = padding + fixedWidth + max(titleWidth, detailWidth)
-        let preferredWidth = max(AppGridUsageTipsMetrics.minWidth, contentWidth)
-        return min(maxAvailableWidth, preferredWidth)
+        max(1, maxAvailableWidth)
     }
 
     private func setup() {
@@ -1084,7 +1110,13 @@
         backgroundView.layer?.borderWidth = 1
         addSubview(backgroundView)
 
-        let iconConfig = NSImage.SymbolConfiguration(pointSize: 24, weight: .semibold)
+        titlePanelView.wantsLayer = true
+        titlePanelView.layer?.cornerRadius = 18
+        titlePanelView.layer?.masksToBounds = true
+        titlePanelView.layer?.borderWidth = 1
+        addSubview(titlePanelView)
+
+        let iconConfig = NSImage.SymbolConfiguration(pointSize: 26, weight: .bold)
         iconView.image = NSImage(
             systemSymbolName: "lightbulb.fill",
             accessibilityDescription: nil
@@ -1092,7 +1124,7 @@
         iconView.imageScaling = .scaleProportionallyDown
         addSubview(iconView)
 
-        configureLabel(titleLabel, font: titleFont, lineBreakMode: .byClipping)
+        configureLabel(titleLabel, font: titleFont, lineBreakMode: .byWordWrapping)
         configureDetailLabel()
 
         detailScrollView.drawsBackground = false
@@ -1102,12 +1134,14 @@
         detailScrollView.borderType = .noBorder
         detailScrollView.documentView = detailLabel
 
+        closeButton.action = { [weak self] in self?.hideUsageTips() }
         previousButton.action = { [weak self] in self?.selectUsageTip(offset: -1) }
         nextButton.action = { [weak self] in self?.selectUsageTip(offset: 1) }
 
         addSubview(titleLabel)
         addSubview(detailScrollView)
         addSubview(dotsView)
+        addSubview(closeButton)
         addSubview(previousButton)
         addSubview(nextButton)
 
@@ -1122,13 +1156,13 @@
         label.drawsBackground = false
         label.isBordered = false
         label.lineBreakMode = lineBreakMode
-        label.maximumNumberOfLines = 1
-        label.alignment = .center
+        label.maximumNumberOfLines = 2
+        label.alignment = .left
         label.font = font
     }
 
     private func configureDetailLabel() {
-        detailLabel.cell = NSTextFieldCell(textCell: "")
+        detailLabel.cell = AppGridCenteredMultilineTextFieldCell(textCell: "")
         detailLabel.isEditable = false
         detailLabel.isSelectable = false
         detailLabel.drawsBackground = false
@@ -1160,9 +1194,22 @@
         applyCoordinatorState()
     }
 
+    private func hideUsageTips() {
+        Diagnostics.log("usageTips.hud.close")
+        clearHoverState()
+        isHidden = true
+        coordinator?.onHideUsageTips()
+    }
+
     private func routeButtonClickIfNeeded(_ event: NSEvent) -> Bool {
         let point = usageTipsPoint(for: event)
-        let hitOutset: CGFloat = 24
+        let closeHitOutset: CGFloat = 8
+        if closeButton.frame.insetBy(dx: -closeHitOutset, dy: -closeHitOutset).contains(point) {
+            closeButton.performPressFeedback()
+            hideUsageTips()
+            return true
+        }
+        let hitOutset: CGFloat = 6
         if nextButton.frame.insetBy(dx: -hitOutset, dy: -hitOutset).contains(point) {
             Diagnostics.log("usageTips.hud.routedNext")
             selectUsageTip(offset: 1)
@@ -1211,14 +1258,6 @@
         detailLabel.frame = NSRect(x: 0, y: 0, width: width, height: detailScrollView.bounds.height)
     }
 
-    private func measuredDetailLineWidth() -> CGFloat {
-        let attributes: [NSAttributedString.Key: Any] = [.font: detailFont]
-        let lineWidths = detailLabel.stringValue
-            .components(separatedBy: .newlines)
-            .map { NSAttributedString(string: $0, attributes: attributes).size().width }
-        return ceil(lineWidths.max() ?? detailLabel.attributedStringValue.size().width)
-    }
-
     private func currentVisualFrame() -> NSRect {
         let width = min(bounds.width, visualWidth)
         let height = min(bounds.height, visualHeight)
@@ -1231,21 +1270,197 @@
     }
 
     private func formattedTipDetail(_ text: String) -> String {
-        text
+        let normalized = text
             .replacingOccurrences(of: "\\s*(?:-->|->|>|→|>|,)\\s*", with: "\n", options: .regularExpression)
             .replacingOccurrences(of: "[ \\t]{2,}", with: " ", options: .regularExpression)
             .replacingOccurrences(of: "\\n{2,}", with: "\n", options: .regularExpression)
             .trimmingCharacters(in: .whitespacesAndNewlines)
+        return orderedTipDetail(normalized)
+    }
+
+    private func orderedTipDetail(_ text: String) -> String {
+        let lines = text
+            .components(separatedBy: .newlines)
+            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
+            .filter { !$0.isEmpty }
+        guard lines.count > 1 else { return text }
+        return lines
+            .enumerated()
+            .map { "\($0.offset + 1). \($0.element)" }
+            .joined(separator: "\n")
+    }
+
+    private func updateTipIcon(id: Int) {
+        let symbolName: String
+        switch id {
+        case 1:
+            symbolName = "tag.fill"
+        case 2:
+            symbolName = "checkmark.seal.fill"
+        case 3:
+            symbolName = "arrow.up.and.down.and.arrow.left.and.right"
+        case 4:
+            symbolName = "doc.on.doc.fill"
+        case 5:
+            symbolName = "minus.circle.fill"
+        case 6:
+            symbolName = "arrow.up.arrow.down.circle.fill"
+        case 7:
+            symbolName = "square.grid.2x2.fill"
+        case 8:
+            symbolName = "note.text"
+        default:
+            symbolName = "lightbulb.fill"
+        }
+        let iconConfig = NSImage.SymbolConfiguration(pointSize: 26, weight: .bold)
+        iconView.image = NSImage(
+            systemSymbolName: symbolName,
+            accessibilityDescription: nil
+        )?.withSymbolConfiguration(iconConfig)
+        ?? NSImage(
+            systemSymbolName: "lightbulb.fill",
+            accessibilityDescription: nil
+        )?.withSymbolConfiguration(iconConfig)
     }
 
     private func updateColors() {
-        backgroundView.layer?.borderColor = NSColor.separatorColor.withAlphaComponent(0.35).cgColor
-        iconView.contentTintColor = .systemYellow
-        titleLabel.textColor = .secondaryLabelColor
-        detailLabel.textColor = .labelColor
-        previousButton.tintColor = .labelColor
-        nextButton.tintColor = .labelColor
-        dotsView.needsDisplay = true
+        let usesDarkGlass = coordinator?.appGridTheme.usesDarkGlass == true
+
+        backgroundView.material = usesDarkGlass ? .underWindowBackground : .popover
+        backgroundView.appearance = usesDarkGlass ? NSAppearance(named: .darkAqua) : nil
+        backgroundView.layer?.backgroundColor = (
+            usesDarkGlass
+                ? NSColor.black.withAlphaComponent(0.46)
+                : NSColor.white.withAlphaComponent(0.54)
+        ).cgColor
+        backgroundView.layer?.borderColor = (
+            usesDarkGlass
+                ? NSColor.white.withAlphaComponent(0.22)
+                : NSColor.black.withAlphaComponent(0.18)
+        ).cgColor
+
+        titlePanelView.layer?.backgroundColor = (
+            usesDarkGlass
+                ? NSColor.white.withAlphaComponent(0.08)
+                : NSColor.white.withAlphaComponent(0.42)
+        ).cgColor
+        titlePanelView.layer?.borderColor = (
+            usesDarkGlass
+                ? NSColor.white.withAlphaComponent(0.12)
+                : NSColor.black.withAlphaComponent(0.08)
+        ).cgColor
+
+        let theme = coordinator?.appGridTheme ?? .fallback
+        let accentColor = usageTipAccentColor(for: theme)
+        iconView.contentTintColor = accentColor
+        titleLabel.textColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.92)
+            : accentColor.withAlphaComponent(0.94)
+        detailLabel.textColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.95)
+            : NSColor(calibratedRed: 0.08, green: 0.10, blue: 0.13, alpha: 0.94)
+        applyDetailTextStyle()
+
+        let buttonTint = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.90)
+            : NSColor(calibratedWhite: 0.08, alpha: 0.92)
+        let buttonHoverTint = usesDarkGlass
+            ? NSColor.white
+            : NSColor(calibratedWhite: 0.04, alpha: 1.00)
+        let buttonPressedTint = usesDarkGlass
+            ? NSColor.white
+            : NSColor.black
+        let buttonSurface = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.10)
+            : NSColor.white.withAlphaComponent(0.38)
+        let buttonHoverSurface = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.20)
+            : NSColor.white.withAlphaComponent(0.70)
+        let buttonPressedSurface = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.28)
+            : NSColor.white.withAlphaComponent(0.86)
+        let buttonBorder = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.14)
+            : NSColor.black.withAlphaComponent(0.10)
+        [previousButton, nextButton].forEach { button in
+            button.tintColor = buttonTint
+            button.hoveredTintColor = buttonHoverTint
+            button.pressedTintColor = buttonPressedTint
+            button.surfaceColor = buttonSurface
+            button.hoveredSurfaceColor = buttonHoverSurface
+            button.pressedSurfaceColor = buttonPressedSurface
+            button.borderColor = buttonBorder
+        }
+
+        closeButton.tintColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.42)
+            : NSColor.black.withAlphaComponent(0.34)
+        closeButton.hoveredTintColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.86)
+            : NSColor.black.withAlphaComponent(0.78)
+        closeButton.pressedTintColor = usesDarkGlass ? .white : .black
+        closeButton.surfaceColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.04)
+            : NSColor.white.withAlphaComponent(0.08)
+        closeButton.hoveredSurfaceColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.14)
+            : NSColor.white.withAlphaComponent(0.62)
+        closeButton.pressedSurfaceColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.22)
+            : NSColor.white.withAlphaComponent(0.82)
+        closeButton.borderColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.08)
+            : NSColor.black.withAlphaComponent(0.08)
+
+        dotsView.selectedColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.88)
+            : NSColor.labelColor.withAlphaComponent(0.74)
+        dotsView.idleColor = usesDarkGlass
+            ? NSColor.white.withAlphaComponent(0.28)
+            : NSColor.labelColor.withAlphaComponent(0.24)
+
+        layer?.shadowOpacity = usesDarkGlass ? 0.28 : 0.14
+        layer?.shadowRadius = usesDarkGlass ? 18 : 12
+        needsDisplay = true
+    }
+
+    private func usageTipAccentColor(for theme: AppGridTheme) -> NSColor {
+        switch theme {
+        case .defaultLight:
+            return NSColor(calibratedRed: 0.34, green: 0.40, blue: 0.48, alpha: 1.00)
+        case .deepBlue:
+            return NSColor(calibratedRed: 0.42, green: 0.80, blue: 1.00, alpha: 1.00)
+        case .black:
+            return NSColor(calibratedRed: 0.36, green: 0.74, blue: 1.00, alpha: 1.00)
+        case .pink:
+            return NSColor(calibratedRed: 0.70, green: 0.18, blue: 0.45, alpha: 1.00)
+        case .purple:
+            return NSColor(calibratedRed: 0.40, green: 0.22, blue: 0.70, alpha: 1.00)
+        case .green:
+            return NSColor(calibratedRed: 0.08, green: 0.42, blue: 0.22, alpha: 1.00)
+        case .blue:
+            return NSColor(calibratedRed: 0.06, green: 0.34, blue: 0.74, alpha: 1.00)
+        case .colorful:
+            return NSColor(calibratedRed: 0.06, green: 0.44, blue: 0.78, alpha: 1.00)
+        }
+    }
+
+    private func applyDetailTextStyle() {
+        let paragraphStyle = NSMutableParagraphStyle()
+        paragraphStyle.alignment = .left
+        paragraphStyle.lineBreakMode = .byWordWrapping
+        paragraphStyle.minimumLineHeight = 31
+        paragraphStyle.maximumLineHeight = 34
+        paragraphStyle.lineSpacing = 6
+
+        detailLabel.attributedStringValue = NSAttributedString(
+            string: currentDetailText,
+            attributes: [
+                .font: detailFont,
+                .foregroundColor: detailLabel.textColor ?? NSColor.labelColor,
+                .paragraphStyle: paragraphStyle
+            ]
+        )
     }
 }
 
@@ -1255,6 +1470,20 @@
         let textHeight = cellSize(forBounds: rect).height
         drawingRect.origin.y = rect.origin.y + max(0, (rect.height - textHeight) / 2)
         drawingRect.size.height = min(rect.height, textHeight + 2)
+        return drawingRect
+    }
+}
+
+private final class AppGridCenteredMultilineTextFieldCell: NSTextFieldCell {
+    override func drawingRect(forBounds rect: NSRect) -> NSRect {
+        var drawingRect = super.drawingRect(forBounds: rect)
+        let textHeight = attributedStringValue.boundingRect(
+            with: NSSize(width: max(1, rect.width), height: .greatestFiniteMagnitude),
+            options: [.usesLineFragmentOrigin, .usesFontLeading]
+        ).height
+        let centeredHeight = min(rect.height, ceil(textHeight) + 4)
+        drawingRect.origin.y = rect.origin.y + max(0, (rect.height - centeredHeight) / 2)
+        drawingRect.size.height = centeredHeight
         return drawingRect
     }
 }
@@ -1278,8 +1507,26 @@
     }
     var tintColor: NSColor = .labelColor {
         didSet {
-            imageView.contentTintColor = tintColor
+            updateIconTint()
         }
+    }
+    var hoveredTintColor: NSColor? {
+        didSet { updateIconTint() }
+    }
+    var pressedTintColor: NSColor? {
+        didSet { updateIconTint() }
+    }
+    var surfaceColor: NSColor = .clear {
+        didSet { needsDisplay = true }
+    }
+    var hoveredSurfaceColor: NSColor = NSColor.labelColor.withAlphaComponent(0.08) {
+        didSet { needsDisplay = true }
+    }
+    var pressedSurfaceColor: NSColor = NSColor.labelColor.withAlphaComponent(0.14) {
+        didSet { needsDisplay = true }
+    }
+    var borderColor: NSColor = .clear {
+        didSet { needsDisplay = true }
     }
 
     private let imageView = AppGridDecorativeImageView()
@@ -1319,9 +1566,20 @@
         return true
     }
 
+    func performPressFeedback() {
+        isPressed = true
+        needsDisplay = true
+        updateIconTint()
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.10) { [weak self] in
+            self?.isPressed = false
+            self?.needsDisplay = true
+            self?.updateIconTint()
+        }
+    }
+
     override func layout() {
         super.layout()
-        let size: CGFloat = 26
+        let size = min(CGFloat(26), max(14, min(bounds.width, bounds.height) * 0.62))
         imageView.frame = NSRect(
             x: (bounds.width - size) / 2,
             y: (bounds.height - size) / 2,
@@ -1352,31 +1610,32 @@
     override func mouseEntered(with event: NSEvent) {
         isHovered = true
         needsDisplay = true
+        updateIconTint()
     }
 
     override func mouseExited(with event: NSEvent) {
         isHovered = false
         isPressed = false
         needsDisplay = true
+        updateIconTint()
     }
 
     override func mouseDown(with event: NSEvent) {
         Diagnostics.log("usageTips.button.mouseDown", [
             "label": buttonAccessibilityLabel
         ])
-        isPressed = true
-        needsDisplay = true
+        performPressFeedback()
         action?()
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.10) { [weak self] in
-            self?.isPressed = false
-            self?.needsDisplay = true
-        }
     }
 
     override func draw(_ dirtyRect: NSRect) {
-        guard isHovered || isPressed else { return }
-        tintColor.withAlphaComponent(isPressed ? 0.14 : 0.08).setFill()
-        NSBezierPath(roundedRect: bounds.insetBy(dx: 1, dy: 1), xRadius: 6, yRadius: 6).fill()
+        let path = NSBezierPath(roundedRect: bounds.insetBy(dx: 1, dy: 1), xRadius: 8, yRadius: 8)
+        let fill = isPressed ? pressedSurfaceColor : isHovered ? hoveredSurfaceColor : surfaceColor
+        fill.setFill()
+        path.fill()
+        borderColor.setStroke()
+        path.lineWidth = 1
+        path.stroke()
     }
 
     private func setup(systemImage: String) {
@@ -1391,11 +1650,27 @@
         addSubview(imageView)
         setAccessibilityRole(.button)
     }
+
+    private func updateIconTint() {
+        if isPressed, let pressedTintColor {
+            imageView.contentTintColor = pressedTintColor
+        } else if isHovered, let hoveredTintColor {
+            imageView.contentTintColor = hoveredTintColor
+        } else {
+            imageView.contentTintColor = tintColor
+        }
+    }
 }
 
 private final class AppGridUsageTipDotsView: NSView {
     private var count = 0
     private var selectedIndex = 0
+    var selectedColor: NSColor = .labelColor {
+        didSet { needsDisplay = true }
+    }
+    var idleColor: NSColor = NSColor.labelColor.withAlphaComponent(0.28) {
+        didSet { needsDisplay = true }
+    }
 
     override var isFlipped: Bool { true }
 
@@ -1420,7 +1695,7 @@
         for index in 0..<count {
             let selected = index == selectedIndex
             let dotSize: CGFloat = selected ? 6 : 5
-            let color = NSColor.labelColor.withAlphaComponent(selected ? 0.85 : 0.28)
+            let color = selected ? selectedColor : idleColor
             color.setFill()
             NSBezierPath(
                 ovalIn: NSRect(
diff --git a/src/Apptag/ContentView.swift b/src/Apptag/ContentView.swift
index 87ffec3..9f0743c 100644
--- a/src/Apptag/ContentView.swift
+++ b/src/Apptag/ContentView.swift
@@ -1242,6 +1242,7 @@
                     },
                     onScrollActivity: handleAppGridScrollActivity,
                     onDragModeChange: { setAppDragMode($0) },
+                    onHideUsageTips: { hideUsageTips = true },
                     onUsageTipsHoverChange: handleUsageTipsHoverChange
                 )
             }
diff --git a/src/Apptag/Info.plist b/src/Apptag/Info.plist
index 3e3fce8..9b25745 100644
--- a/src/Apptag/Info.plist
+++ b/src/Apptag/Info.plist
@@ -19,9 +19,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>8.0.1</string>
+	<string>8.0.2</string>
 	<key>CFBundleVersion</key>
-	<string>20260624.2021</string>
+	<string>20260624.2341</string>
 	<key>LSApplicationCategoryType</key>
 	<string>public.app-category.utilities</string>
 	<key>LSMinimumSystemVersion</key>
diff --git a/src/Apptag/Localization/ar-Najdi.json b/src/Apptag/Localization/ar-Najdi.json
index 9280fba..aecd821 100644
--- a/src/Apptag/Localization/ar-Najdi.json
+++ b/src/Apptag/Localization/ar-Najdi.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "إذا شغّلته، تختفي التلميحات العايمة أسفل شبكة التطبيقات.",
   "usageTips.previous": "التلميحة السابقة",
   "usageTips.next": "التلميحة التالية",
-  "usageTips.tip1.title": "تلميحة 1 - إضافة وسوم",
-  "usageTips.tip1.detail": "افتح الإعدادات, عدّل الوسوم.",
+  "usageTips.close": "إغلاق التلميحات",
+  "usageTips.tip1.title": "تلميحة 1 - تحرير الوسوم",
+  "usageTips.tip1.detail": "افتح الإعدادات وانتقل إلى الوسوم للتحرير, انقر نقرًا مزدوجًا على وسم في قائمة الوسوم لفتح تحرير الوسوم بسرعة.",
   "usageTips.tip2.title": "تلميحة 2 - وسم التطبيقات",
   "usageTips.tip2.detail": "اضغط مطوّل على التطبيق, اسحبه على وسم.",
   "usageTips.tip3.title": "تلميحة 3 - نقل التطبيقات",
diff --git a/src/Apptag/Localization/ar.json b/src/Apptag/Localization/ar.json
index 631b3e1..ed8b24e 100644
--- a/src/Apptag/Localization/ar.json
+++ b/src/Apptag/Localization/ar.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "عند التفعيل، يتم إخفاء النصائح العائمة أسفل شبكة التطبيقات.",
   "usageTips.previous": "النصيحة السابقة",
   "usageTips.next": "النصيحة التالية",
-  "usageTips.tip1.title": "النصيحة 1 - إضافة وسوم",
-  "usageTips.tip1.detail": "افتح الإعدادات, حرر الوسوم.",
+  "usageTips.close": "إغلاق التلميحات",
+  "usageTips.tip1.title": "النصيحة 1 - تحرير الوسوم",
+  "usageTips.tip1.detail": "افتح الإعدادات وانتقل إلى الوسوم للتحرير, انقر نقرًا مزدوجًا على وسم في قائمة الوسوم لفتح تحرير الوسوم بسرعة.",
   "usageTips.tip2.title": "النصيحة 2 - وسم التطبيقات",
   "usageTips.tip2.detail": "اضغط مطولاً على تطبيق, اسحبه إلى وسم.",
   "usageTips.tip3.title": "النصيحة 3 - نقل التطبيقات",
diff --git a/src/Apptag/Localization/cs.json b/src/Apptag/Localization/cs.json
index 2c1c042..373c3f7 100644
--- a/src/Apptag/Localization/cs.json
+++ b/src/Apptag/Localization/cs.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Když je zapnuto, plovoucí tipy dole v mřížce aplikací se skryjí.",
   "usageTips.previous": "Předchozí tip",
   "usageTips.next": "Další tip",
-  "usageTips.tip1.title": "Tip 1 - Přidat tagy",
-  "usageTips.tip1.detail": "Otevřete Nastavení, upravte tagy.",
+  "usageTips.close": "Zavřít tipy",
+  "usageTips.tip1.title": "Tip 1 - Upravit tagy",
+  "usageTips.tip1.detail": "Otevřete Nastavení a přepněte na Tagy pro úpravy, Dvojklikem na tag v seznamu rychle otevřete úpravy tagů.",
   "usageTips.tip2.title": "Tip 2 - Označit aplikace",
   "usageTips.tip2.detail": "Podržte aplikaci, přetáhněte ji na tag.",
   "usageTips.tip3.title": "Tip 3 - Přesunout aplikace",
diff --git a/src/Apptag/Localization/da.json b/src/Apptag/Localization/da.json
index 8df55ad..5aac946 100644
--- a/src/Apptag/Localization/da.json
+++ b/src/Apptag/Localization/da.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Når det er slået til, skjules de flydende tips nederst i appgitteret.",
   "usageTips.previous": "Forrige tip",
   "usageTips.next": "Næste tip",
-  "usageTips.tip1.title": "Tip 1 - Tilføj tags",
-  "usageTips.tip1.detail": "Åbn Indstillinger, rediger tags.",
+  "usageTips.close": "Luk tips",
+  "usageTips.tip1.title": "Tip 1 - Rediger tags",
+  "usageTips.tip1.detail": "Åbn Indstillinger og skift til Tags for at redigere, Dobbeltklik på et tag i taglisten for hurtigt at åbne tagredigering.",
   "usageTips.tip2.title": "Tip 2 - Tag apps",
   "usageTips.tip2.detail": "Hold en app nede, slip den på et tag.",
   "usageTips.tip3.title": "Tip 3 - Flyt apps",
diff --git a/src/Apptag/Localization/de.json b/src/Apptag/Localization/de.json
index 1bf578f..ff27951 100644
--- a/src/Apptag/Localization/de.json
+++ b/src/Apptag/Localization/de.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Wenn aktiviert, werden die schwebenden Tipps unten im App-Raster ausgeblendet.",
   "usageTips.previous": "Vorheriger Tipp",
   "usageTips.next": "Nächster Tipp",
-  "usageTips.tip1.title": "Tipp 1 - Tags hinzufügen",
-  "usageTips.tip1.detail": "Einstellungen öffnen, Tags bearbeiten.",
+  "usageTips.close": "Tipps schließen",
+  "usageTips.tip1.title": "Tipp 1 - Tags bearbeiten",
+  "usageTips.tip1.detail": "Öffnen Sie Einstellungen und wechseln Sie zu Tags zum Bearbeiten, Doppelklicken Sie in der Tagliste auf einen Tag zum schnellen Bearbeiten.",
   "usageTips.tip2.title": "Tipp 2 - Apps taggen",
   "usageTips.tip2.detail": "App gedrückt halten, auf einen Tag ziehen.",
   "usageTips.tip3.title": "Tipp 3 - Apps verschieben",
diff --git a/src/Apptag/Localization/en.json b/src/Apptag/Localization/en.json
index 78b7151..b9e414d 100644
--- a/src/Apptag/Localization/en.json
+++ b/src/Apptag/Localization/en.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "When enabled, the floating tips at the bottom of the app grid are hidden.",
   "usageTips.previous": "Previous tip",
   "usageTips.next": "Next tip",
-  "usageTips.tip1.title": "Tip 1 - Add tags",
-  "usageTips.tip1.detail": "Open Settings, edit tags.",
+  "usageTips.close": "Close tips",
+  "usageTips.tip1.title": "Tip 1 - Edit tags",
+  "usageTips.tip1.detail": "Open Settings and switch to Tags to edit, Double-click a tag in the tag list to open tag editing quickly.",
   "usageTips.tip2.title": "Tip 2 - Tag apps",
   "usageTips.tip2.detail": "Long-press an app, then drag it onto a tag.",
   "usageTips.tip3.title": "Tip 3 - Move apps",
diff --git a/src/Apptag/Localization/es.json b/src/Apptag/Localization/es.json
index 1a39921..504e016 100644
--- a/src/Apptag/Localization/es.json
+++ b/src/Apptag/Localization/es.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Si está activado, se ocultan los consejos flotantes de la parte inferior de la cuadrícula de apps.",
   "usageTips.previous": "Consejo anterior",
   "usageTips.next": "Consejo siguiente",
-  "usageTips.tip1.title": "Consejo 1 - Añadir etiquetas",
-  "usageTips.tip1.detail": "Abra Ajustes, edite etiquetas.",
+  "usageTips.close": "Cerrar consejos",
+  "usageTips.tip1.title": "Consejo 1 - Editar etiquetas",
+  "usageTips.tip1.detail": "Abra Ajustes y cambie a Etiquetas para editar, Haga doble clic en una etiqueta de la lista para abrir rápido la edición.",
   "usageTips.tip2.title": "Consejo 2 - Etiquetar apps",
   "usageTips.tip2.detail": "Mantén pulsada una app, arrástrala a una etiqueta.",
   "usageTips.tip3.title": "Consejo 3 - Mover apps",
diff --git a/src/Apptag/Localization/fr.json b/src/Apptag/Localization/fr.json
index 8bb733f..ce30d53 100644
--- a/src/Apptag/Localization/fr.json
+++ b/src/Apptag/Localization/fr.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Si cette option est activée, les astuces flottantes en bas de la grille des apps sont masquées.",
   "usageTips.previous": "Astuce précédente",
   "usageTips.next": "Astuce suivante",
-  "usageTips.tip1.title": "Astuce 1 - Ajouter des tags",
-  "usageTips.tip1.detail": "Ouvrez Réglages, modifiez les tags.",
+  "usageTips.close": "Fermer les astuces",
+  "usageTips.tip1.title": "Astuce 1 - Modifier les tags",
+  "usageTips.tip1.detail": "Ouvrez Réglages et passez à Tags pour modifier, Double-cliquez sur un tag dans la liste pour ouvrir vite l’édition.",
   "usageTips.tip2.title": "Astuce 2 - Taguer des apps",
   "usageTips.tip2.detail": "Appuyez longuement sur une app, déposez-la sur un tag.",
   "usageTips.tip3.title": "Astuce 3 - Déplacer des apps",
diff --git a/src/Apptag/Localization/id.json b/src/Apptag/Localization/id.json
index 42fb22d..83a2029 100644
--- a/src/Apptag/Localization/id.json
+++ b/src/Apptag/Localization/id.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Jika aktif, tips mengambang di bagian bawah grid app akan disembunyikan.",
   "usageTips.previous": "Tips sebelumnya",
   "usageTips.next": "Tips berikutnya",
-  "usageTips.tip1.title": "Tips 1 - Tambah tag",
-  "usageTips.tip1.detail": "Buka Pengaturan, edit tag.",
+  "usageTips.close": "Tutup tips",
+  "usageTips.tip1.title": "Tips 1 - Edit tag",
+  "usageTips.tip1.detail": "Buka Pengaturan lalu pindah ke Tag untuk mengedit, Klik dua kali tag di daftar tag untuk cepat membuka editor tag.",
   "usageTips.tip2.title": "Tips 2 - Beri tag app",
   "usageTips.tip2.detail": "Tekan lama app, jatuhkan ke tag.",
   "usageTips.tip3.title": "Tips 3 - Pindahkan app",
diff --git a/src/Apptag/Localization/it.json b/src/Apptag/Localization/it.json
index 0acc575..4aec5ab 100644
--- a/src/Apptag/Localization/it.json
+++ b/src/Apptag/Localization/it.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Se attivo, i suggerimenti flottanti in basso nella griglia delle app vengono nascosti.",
   "usageTips.previous": "Suggerimento precedente",
   "usageTips.next": "Suggerimento successivo",
-  "usageTips.tip1.title": "Suggerimento 1 - Aggiungere tag",
-  "usageTips.tip1.detail": "Apri Impostazioni, modifica i tag.",
+  "usageTips.close": "Chiudi suggerimenti",
+  "usageTips.tip1.title": "Suggerimento 1 - Modificare i tag",
+  "usageTips.tip1.detail": "Apri Impostazioni e passa a Tag per modificare, Fai doppio clic su un tag nell’elenco per aprire rapidamente la modifica.",
   "usageTips.tip2.title": "Suggerimento 2 - Taggare app",
   "usageTips.tip2.detail": "Tieni premuta un’app, rilasciala su un tag.",
   "usageTips.tip3.title": "Suggerimento 3 - Spostare app",
diff --git a/src/Apptag/Localization/ja.json b/src/Apptag/Localization/ja.json
index 24580dc..6fdd118 100644
--- a/src/Apptag/Localization/ja.json
+++ b/src/Apptag/Localization/ja.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "オンにすると、アプリグリッド下部のフローティングヒントを表示しません。",
   "usageTips.previous": "前のヒント",
   "usageTips.next": "次のヒント",
-  "usageTips.tip1.title": "ヒント1 - タグを追加",
-  "usageTips.tip1.detail": "設定を開く, タグを編集します。",
+  "usageTips.close": "ヒントを閉じる",
+  "usageTips.tip1.title": "ヒント1 - タグを編集",
+  "usageTips.tip1.detail": "設定を開いて「タグ」に切り替えて編集, タグ一覧でタグをダブルクリックすると編集画面をすぐ開けます。",
   "usageTips.tip2.title": "ヒント2 - アプリにタグ付け",
   "usageTips.tip2.detail": "アプリを長押し, タグ上にドロップします。",
   "usageTips.tip3.title": "ヒント3 - アプリを移動",
diff --git a/src/Apptag/Localization/ko.json b/src/Apptag/Localization/ko.json
index bfcca64..61b14b8 100644
--- a/src/Apptag/Localization/ko.json
+++ b/src/Apptag/Localization/ko.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "켜면 앱 그리드 하단의 플로팅 사용 팁이 표시되지 않습니다.",
   "usageTips.previous": "이전 팁",
   "usageTips.next": "다음 팁",
-  "usageTips.tip1.title": "팁 1 - 태그 추가",
-  "usageTips.tip1.detail": "설정 열기, 태그 편집.",
+  "usageTips.close": "팁 닫기",
+  "usageTips.tip1.title": "팁 1 - 태그 편집",
+  "usageTips.tip1.detail": "설정을 열고 태그로 전환해 편집, 태그 목록에서 태그를 두 번 클릭하면 태그 편집으로 빠르게 이동합니다.",
   "usageTips.tip2.title": "팁 2 - 앱에 태그 지정",
   "usageTips.tip2.detail": "앱을 길게 누르기, 태그 위에 놓기.",
   "usageTips.tip3.title": "팁 3 - 앱 이동",
diff --git a/src/Apptag/Localization/ms.json b/src/Apptag/Localization/ms.json
index aed2ea8..a678e40 100644
--- a/src/Apptag/Localization/ms.json
+++ b/src/Apptag/Localization/ms.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Apabila dihidupkan, petua terapung di bahagian bawah grid app akan disembunyikan.",
   "usageTips.previous": "Petua sebelumnya",
   "usageTips.next": "Petua seterusnya",
-  "usageTips.tip1.title": "Petua 1 - Tambah tag",
-  "usageTips.tip1.detail": "Buka Tetapan, edit tag.",
+  "usageTips.close": "Tutup petua",
+  "usageTips.tip1.title": "Petua 1 - Edit tag",
+  "usageTips.tip1.detail": "Buka Tetapan dan tukar ke Tag untuk mengedit, Dwiklik tag dalam senarai tag untuk cepat membuka editor tag.",
   "usageTips.tip2.title": "Petua 2 - Tag app",
   "usageTips.tip2.detail": "Tekan lama app, lepaskan pada tag.",
   "usageTips.tip3.title": "Petua 3 - Alih app",
diff --git a/src/Apptag/Localization/nb.json b/src/Apptag/Localization/nb.json
index 037ffb7..ec86d9e 100644
--- a/src/Apptag/Localization/nb.json
+++ b/src/Apptag/Localization/nb.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Når dette er slått på, skjules de flytende tipsene nederst i app-rutenettet.",
   "usageTips.previous": "Forrige tips",
   "usageTips.next": "Neste tips",
-  "usageTips.tip1.title": "Tips 1 - Legg til tagger",
-  "usageTips.tip1.detail": "Åpne Innstillinger, rediger tagger.",
+  "usageTips.close": "Lukk tips",
+  "usageTips.tip1.title": "Tips 1 - Rediger tagger",
+  "usageTips.tip1.detail": "Åpne Innstillinger og bytt til Tagger for å redigere, Dobbeltklikk en tagg i tagglisten for raskt å åpne redigering.",
   "usageTips.tip2.title": "Tips 2 - Tagg apper",
   "usageTips.tip2.detail": "Hold en app inne, slipp den på en tagg.",
   "usageTips.tip3.title": "Tips 3 - Flytt apper",
diff --git a/src/Apptag/Localization/nl.json b/src/Apptag/Localization/nl.json
index 5436b0d..7ed9eda 100644
--- a/src/Apptag/Localization/nl.json
+++ b/src/Apptag/Localization/nl.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Als dit is ingeschakeld, worden de zwevende tips onderaan het app-raster verborgen.",
   "usageTips.previous": "Vorige tip",
   "usageTips.next": "Volgende tip",
-  "usageTips.tip1.title": "Tip 1 - Tags toevoegen",
-  "usageTips.tip1.detail": "Open Instellingen, bewerk tags.",
+  "usageTips.close": "Tips sluiten",
+  "usageTips.tip1.title": "Tip 1 - Tags bewerken",
+  "usageTips.tip1.detail": "Open Instellingen en ga naar Tags om te bewerken, Dubbelklik op een tag in de lijst om tagbewerking snel te openen.",
   "usageTips.tip2.title": "Tip 2 - Apps taggen",
   "usageTips.tip2.detail": "Houd een app ingedrukt, sleep naar een tag.",
   "usageTips.tip3.title": "Tip 3 - Apps verplaatsen",
diff --git a/src/Apptag/Localization/nn.json b/src/Apptag/Localization/nn.json
index e6671c0..acd04e1 100644
--- a/src/Apptag/Localization/nn.json
+++ b/src/Apptag/Localization/nn.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Når dette er slått på, vert dei flytande tipsa nedst i apprutenettet gøymde.",
   "usageTips.previous": "Forrige tips",
   "usageTips.next": "Neste tips",
-  "usageTips.tip1.title": "Tips 1 - Legg til tagger",
-  "usageTips.tip1.detail": "Opna Innstillingar, rediger taggar.",
+  "usageTips.close": "Lukk tips",
+  "usageTips.tip1.title": "Tips 1 - Rediger taggar",
+  "usageTips.tip1.detail": "Opna Innstillingar og byt til Taggar for å redigere, Dobbeltklikk ein tagg i tagglista for raskt å opne redigering.",
   "usageTips.tip2.title": "Tips 2 - Tagg apper",
   "usageTips.tip2.detail": "Hold ein app inne, slepp han på ein tagg.",
   "usageTips.tip3.title": "Tips 3 - Flytt apper",
diff --git a/src/Apptag/Localization/no.json b/src/Apptag/Localization/no.json
index 037ffb7..ec86d9e 100644
--- a/src/Apptag/Localization/no.json
+++ b/src/Apptag/Localization/no.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Når dette er slått på, skjules de flytende tipsene nederst i app-rutenettet.",
   "usageTips.previous": "Forrige tips",
   "usageTips.next": "Neste tips",
-  "usageTips.tip1.title": "Tips 1 - Legg til tagger",
-  "usageTips.tip1.detail": "Åpne Innstillinger, rediger tagger.",
+  "usageTips.close": "Lukk tips",
+  "usageTips.tip1.title": "Tips 1 - Rediger tagger",
+  "usageTips.tip1.detail": "Åpne Innstillinger og bytt til Tagger for å redigere, Dobbeltklikk en tagg i tagglisten for raskt å åpne redigering.",
   "usageTips.tip2.title": "Tips 2 - Tagg apper",
   "usageTips.tip2.detail": "Hold en app inne, slipp den på en tagg.",
   "usageTips.tip3.title": "Tips 3 - Flytt apper",
diff --git a/src/Apptag/Localization/pl.json b/src/Apptag/Localization/pl.json
index 8771abd..8404df6 100644
--- a/src/Apptag/Localization/pl.json
+++ b/src/Apptag/Localization/pl.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Po włączeniu pływające wskazówki u dołu siatki aplikacji będą ukryte.",
   "usageTips.previous": "Poprzednia wskazówka",
   "usageTips.next": "Następna wskazówka",
-  "usageTips.tip1.title": "Wskazówka 1 - Dodaj tagi",
-  "usageTips.tip1.detail": "Otwórz Ustawienia, edytuj tagi.",
+  "usageTips.close": "Zamknij wskazówki",
+  "usageTips.tip1.title": "Wskazówka 1 - Edytuj tagi",
+  "usageTips.tip1.detail": "Otwórz Ustawienia i przejdź do Tagów aby edytować, Kliknij dwukrotnie tag na liście aby szybko otworzyć edycję.",
   "usageTips.tip2.title": "Wskazówka 2 - Otaguj aplikacje",
   "usageTips.tip2.detail": "Przytrzymaj aplikację, przeciągnij ją na tag.",
   "usageTips.tip3.title": "Wskazówka 3 - Przenieś aplikacje",
diff --git a/src/Apptag/Localization/pt-BR.json b/src/Apptag/Localization/pt-BR.json
index 0379664..0c357fd 100644
--- a/src/Apptag/Localization/pt-BR.json
+++ b/src/Apptag/Localization/pt-BR.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Quando ativado, as dicas flutuantes na parte inferior da grade de apps ficam ocultas.",
   "usageTips.previous": "Dica anterior",
   "usageTips.next": "Próxima dica",
-  "usageTips.tip1.title": "Dica 1 - Adicionar tags",
-  "usageTips.tip1.detail": "Abra Ajustes, edite tags.",
+  "usageTips.close": "Fechar dicas",
+  "usageTips.tip1.title": "Dica 1 - Editar tags",
+  "usageTips.tip1.detail": "Abra Ajustes e mude para Tags para editar, Clique duas vezes em uma tag na lista para abrir a edição rapidamente.",
   "usageTips.tip2.title": "Dica 2 - Marcar apps",
   "usageTips.tip2.detail": "Pressione e segure um app, solte sobre uma tag.",
   "usageTips.tip3.title": "Dica 3 - Mover apps",
diff --git a/src/Apptag/Localization/ro.json b/src/Apptag/Localization/ro.json
index 252df0f..ef254b1 100644
--- a/src/Apptag/Localization/ro.json
+++ b/src/Apptag/Localization/ro.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Când este activat, sfaturile plutitoare din partea de jos a grilei de aplicații sunt ascunse.",
   "usageTips.previous": "Sfatul anterior",
   "usageTips.next": "Sfatul următor",
-  "usageTips.tip1.title": "Sfat 1 - Adaugă etichete",
-  "usageTips.tip1.detail": "Deschide Setări, editează etichetele.",
+  "usageTips.close": "Închide sfaturile",
+  "usageTips.tip1.title": "Sfat 1 - Editează etichete",
+  "usageTips.tip1.detail": "Deschide Setări și comută la Etichete pentru editare, Dublu clic pe o etichetă din listă pentru a deschide rapid editarea.",
   "usageTips.tip2.title": "Sfat 2 - Etichetează aplicații",
   "usageTips.tip2.detail": "Ține apăsată o aplicație, plaseaz-o pe o etichetă.",
   "usageTips.tip3.title": "Sfat 3 - Mută aplicații",
diff --git a/src/Apptag/Localization/ru.json b/src/Apptag/Localization/ru.json
index 6acba70..60577a4 100644
--- a/src/Apptag/Localization/ru.json
+++ b/src/Apptag/Localization/ru.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Если включено, плавающие советы внизу сетки приложений будут скрыты.",
   "usageTips.previous": "Предыдущий совет",
   "usageTips.next": "Следующий совет",
-  "usageTips.tip1.title": "Совет 1 — Добавить теги",
-  "usageTips.tip1.detail": "Откройте Настройки, редактируйте теги.",
+  "usageTips.close": "Закрыть советы",
+  "usageTips.tip1.title": "Совет 1 — Редактировать теги",
+  "usageTips.tip1.detail": "Откройте Настройки и перейдите к Тегам для редактирования, Дважды щёлкните тег в списке чтобы быстро открыть редактирование.",
   "usageTips.tip2.title": "Совет 2 — Назначить теги",
   "usageTips.tip2.detail": "Удерживайте приложение, перетащите на тег.",
   "usageTips.tip3.title": "Совет 3 — Переместить приложения",
diff --git a/src/Apptag/Localization/sr-Cyrl.json b/src/Apptag/Localization/sr-Cyrl.json
index ad7af39..dc48ed0 100644
--- a/src/Apptag/Localization/sr-Cyrl.json
+++ b/src/Apptag/Localization/sr-Cyrl.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Када је укључено, плутајући савети на дну мреже апликација су сакривени.",
   "usageTips.previous": "Претходни савет",
   "usageTips.next": "Следећи савет",
-  "usageTips.tip1.title": "Савет 1 - Додај ознаке",
-  "usageTips.tip1.detail": "Отворите Подешавања, уредите ознаке.",
+  "usageTips.close": "Затвори савете",
+  "usageTips.tip1.title": "Савет 1 - Уреди ознаке",
+  "usageTips.tip1.detail": "Отворите Подешавања и пређите на Ознаке за уређивање, Двапут кликните ознаку у листи за брзо отварање уређивања.",
   "usageTips.tip2.title": "Савет 2 - Означи апликације",
   "usageTips.tip2.detail": "Држите апликацију, превуците је на ознаку.",
   "usageTips.tip3.title": "Савет 3 - Премести апликације",
diff --git a/src/Apptag/Localization/sv.json b/src/Apptag/Localization/sv.json
index 368bf08..f8a0ae1 100644
--- a/src/Apptag/Localization/sv.json
+++ b/src/Apptag/Localization/sv.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "När detta är aktiverat döljs de flytande tipsen längst ned i apprutnätet.",
   "usageTips.previous": "Föregående tips",
   "usageTips.next": "Nästa tips",
-  "usageTips.tip1.title": "Tips 1 - Lägg till taggar",
-  "usageTips.tip1.detail": "Öppna Inställningar, redigera taggar.",
+  "usageTips.close": "Stäng tips",
+  "usageTips.tip1.title": "Tips 1 - Redigera taggar",
+  "usageTips.tip1.detail": "Öppna Inställningar och växla till Taggar för att redigera, Dubbelklicka på en tagg i tagglistan för att snabbt öppna redigering.",
   "usageTips.tip2.title": "Tips 2 - Tagga appar",
   "usageTips.tip2.detail": "Håll ned en app, släpp den på en tagg.",
   "usageTips.tip3.title": "Tips 3 - Flytta appar",
diff --git a/src/Apptag/Localization/th.json b/src/Apptag/Localization/th.json
index b0b8205..d97847e 100644
--- a/src/Apptag/Localization/th.json
+++ b/src/Apptag/Localization/th.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "เมื่อเปิดใช้ เคล็ดลับแบบลอยที่ด้านล่างของกริดแอปจะถูกซ่อน",
   "usageTips.previous": "เคล็ดลับก่อนหน้า",
   "usageTips.next": "เคล็ดลับถัดไป",
-  "usageTips.tip1.title": "เคล็ดลับ 1 - เพิ่มแท็ก",
-  "usageTips.tip1.detail": "เปิดการตั้งค่า, แก้ไขแท็ก",
+  "usageTips.close": "ปิดเคล็ดลับ",
+  "usageTips.tip1.title": "เคล็ดลับ 1 - แก้ไขแท็ก",
+  "usageTips.tip1.detail": "เปิดการตั้งค่าแล้วไปที่แท็กเพื่อแก้ไข, ดับเบิลคลิกแท็กในรายการแท็กเพื่อเปิดการแก้ไขแท็กอย่างรวดเร็ว",
   "usageTips.tip2.title": "เคล็ดลับ 2 - ติดแท็กแอป",
   "usageTips.tip2.detail": "กดแอปค้างไว้, วางบนแท็ก",
   "usageTips.tip3.title": "เคล็ดลับ 3 - ย้ายแอป",
diff --git a/src/Apptag/Localization/tr.json b/src/Apptag/Localization/tr.json
index dfd4535..8e58f5f 100644
--- a/src/Apptag/Localization/tr.json
+++ b/src/Apptag/Localization/tr.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Etkinleştirildiğinde uygulama ızgarasının altındaki yüzen ipuçları gizlenir.",
   "usageTips.previous": "Önceki ipucu",
   "usageTips.next": "Sonraki ipucu",
-  "usageTips.tip1.title": "İpucu 1 - Etiket ekle",
-  "usageTips.tip1.detail": "Ayarları açın, etiketleri düzenleyin.",
+  "usageTips.close": "İpuçlarını kapat",
+  "usageTips.tip1.title": "İpucu 1 - Etiketleri düzenle",
+  "usageTips.tip1.detail": "Ayarları açın ve düzenlemek için Etiketler’e geçin, Etiket listesindeki bir etikete çift tıklayarak düzenlemeyi hızlı açın.",
   "usageTips.tip2.title": "İpucu 2 - Uygulamaları etiketle",
   "usageTips.tip2.detail": "Bir uygulamaya uzun basın, etikete bırakın.",
   "usageTips.tip3.title": "İpucu 3 - Uygulamaları taşı",
diff --git a/src/Apptag/Localization/uk.json b/src/Apptag/Localization/uk.json
index cbcbc9d..54d9cfb 100644
--- a/src/Apptag/Localization/uk.json
+++ b/src/Apptag/Localization/uk.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Якщо ввімкнено, плаваючі поради внизу сітки програм буде приховано.",
   "usageTips.previous": "Попередня порада",
   "usageTips.next": "Наступна порада",
-  "usageTips.tip1.title": "Порада 1 — Додати теги",
-  "usageTips.tip1.detail": "Відкрийте Параметри, редагуйте теги.",
+  "usageTips.close": "Закрити поради",
+  "usageTips.tip1.title": "Порада 1 — Редагувати теги",
+  "usageTips.tip1.detail": "Відкрийте Параметри й перейдіть до Тегів для редагування, Двічі клацніть тег у списку щоб швидко відкрити редагування.",
   "usageTips.tip2.title": "Порада 2 — Позначити програми",
   "usageTips.tip2.detail": "Утримуйте програму, перетягніть її на тег.",
   "usageTips.tip3.title": "Порада 3 — Перемістити програми",
diff --git a/src/Apptag/Localization/vi.json b/src/Apptag/Localization/vi.json
index 6914e90..77551f2 100644
--- a/src/Apptag/Localization/vi.json
+++ b/src/Apptag/Localization/vi.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "Khi bật, các mẹo nổi ở cuối lưới ứng dụng sẽ bị ẩn.",
   "usageTips.previous": "Mẹo trước",
   "usageTips.next": "Mẹo tiếp theo",
-  "usageTips.tip1.title": "Mẹo 1 - Thêm thẻ",
-  "usageTips.tip1.detail": "Mở Cài đặt, chỉnh sửa thẻ.",
+  "usageTips.close": "Đóng mẹo",
+  "usageTips.tip1.title": "Mẹo 1 - Sửa thẻ",
+  "usageTips.tip1.detail": "Mở Cài đặt và chuyển sang Thẻ để chỉnh sửa, Nhấp đúp một thẻ trong danh sách để mở nhanh phần chỉnh sửa.",
   "usageTips.tip2.title": "Mẹo 2 - Gắn thẻ ứng dụng",
   "usageTips.tip2.detail": "Nhấn giữ ứng dụng, thả lên thẻ.",
   "usageTips.tip3.title": "Mẹo 3 - Di chuyển ứng dụng",
diff --git a/src/Apptag/Localization/zh-Hans.json b/src/Apptag/Localization/zh-Hans.json
index db1bfa2..c4cc7e3 100644
--- a/src/Apptag/Localization/zh-Hans.json
+++ b/src/Apptag/Localization/zh-Hans.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "开启后,应用网格底部的悬浮使用技巧将不再显示。",
   "usageTips.previous": "上一条技巧",
   "usageTips.next": "下一条技巧",
-  "usageTips.tip1.title": "技巧1-添加标签",
-  "usageTips.tip1.detail": "在设置 > 切换到“标签”即可编辑",
+  "usageTips.close": "关闭使用技巧",
+  "usageTips.tip1.title": "技巧1-编辑标签",
+  "usageTips.tip1.detail": "打开设置,切换到“标签”即可编辑\n在标签列表,双击标签即可快速进入标签编辑页面",
   "usageTips.tip2.title": "技巧2-打标签",
   "usageTips.tip2.detail": "长按应用图标启动拖动 > 拖动到标签上是打标签",
   "usageTips.tip3.title": "技巧3-移动应用",
diff --git a/src/Apptag/Localization/zh-Hant.json b/src/Apptag/Localization/zh-Hant.json
index dba0c49..316288e 100644
--- a/src/Apptag/Localization/zh-Hant.json
+++ b/src/Apptag/Localization/zh-Hant.json
@@ -215,8 +215,9 @@
   "settings.hideUsageTipsDesc": "開啟後,應用程式網格底部的浮動使用技巧將不再顯示。",
   "usageTips.previous": "上一則技巧",
   "usageTips.next": "下一則技巧",
-  "usageTips.tip1.title": "技巧1-新增標籤",
-  "usageTips.tip1.detail": "在設定 > 切換到「標籤」即可編輯",
+  "usageTips.close": "關閉使用技巧",
+  "usageTips.tip1.title": "技巧1-編輯標籤",
+  "usageTips.tip1.detail": "打開設定,切換到「標籤」即可編輯\n在標籤列表,雙擊標籤即可快速進入標籤編輯頁面",
   "usageTips.tip2.title": "技巧2-套用標籤",
   "usageTips.tip2.detail": "長按應用程式圖示開始拖曳 > 拖到標籤上即可套用標籤",
   "usageTips.tip3.title": "技巧3-移動應用",
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index 31ae0c8..1a2befb 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -1,5 +1,16 @@
 # TagLauncher Changelog
 
+## [8.0.2] — 2026-06-24
+
+- 重设计 App Grid 底部使用技巧:改为原生 AppKit 分区式教学横幅,底部横向占满可用宽度,左侧为技巧编号与标题,中央为两行动作说明,右侧为上一条/下一条控制与页点
+- 使用技巧横幅接入主题玻璃 token:默认、粉色、紫色、绿色、蓝色、炫彩等亮色主题使用浅玻璃配色;深蓝和黑色主题使用深色玻璃材质与白色文字,保证可读性
+- 优化使用技巧横幅标题区:标题字号加大并加粗,标题和图标按当前主题使用高对比 accent;每条技巧使用不同语义图标,正文多段说明显示为编号步骤
+- 更新第 1 条使用技巧为“编辑标签”:说明可在设置页切换到“标签”编辑,也可在标签列表双击标签快速进入标签编辑页;同步补齐 29 个语种文案
+- 新增低视觉权重关闭按钮:默认半透明,hover 后更清晰;点击后持久关闭使用技巧,与设置页“隐藏使用技巧”一致
+- 保持使用技巧区域的全宽事件拦截:点击横幅和其底部事件区域不会穿透到底层 App Grid,也不会误触发应用气泡或打开应用;只有命中箭头区域才切换技巧
+- 补齐 29 个语种的 `usageTips.close` 文案,并更新 `Scripts/usage_tips_qa.sh`,覆盖分区布局、亮/暗主题配色、关闭动作、事件拦截、macOS 14 兼容和 29 语种文案完整性
+- 版本号更新为 `8.0.2`,Build 更新为 `20260624.2341`
+
 ## [8.0.1] — 2026-06-24
 
 - 新增 App Grid 主题系统:设置页新增“主题”页签,可在默认、深蓝、黑色、粉色、紫色、绿色、蓝色、炫彩 8 个主题之间切换
diff --git a/src/Docs/Requirements/2026-06-24-app-grid-theme-system-todo.md b/src/Docs/Requirements/2026-06-24-app-grid-theme-system-todo.md
index bc0ce62..43b1836 100644
--- a/src/Docs/Requirements/2026-06-24-app-grid-theme-system-todo.md
+++ b/src/Docs/Requirements/2026-06-24-app-grid-theme-system-todo.md
@@ -29,6 +29,12 @@
 - [x] 移除旧“启用深色视图”设置入口和本地化文案。
 - [x] 新增主题设置 QA 脚本。
 - [x] 更新 changelog、TODO 和工作日志。
+- [x] 使用技巧横幅重设计:按分区式教学横幅落地,亮色主题用浅玻璃,深蓝/黑色主题用深玻璃和白字。
+- [x] 使用技巧新增弱视觉关闭按钮,hover 后清晰,关闭后持久写入 `hideUsageTips`。
+- [x] 使用技巧 QA 增加分区布局、主题配色、关闭动作、全宽事件拦截和 29 语种 `usageTips.close` 检查。
+- [x] 使用技巧标题区视觉优化:标题放大加粗、按主题使用高对比 accent、按技巧类型切换语义 icon。
+- [x] 使用技巧正文多段说明改为编号步骤;第 1 条改为“编辑标签”,并补充双击标签快速进入标签编辑页。
+- [x] 同步更新第 1 条使用技巧 29 个语种文案,并把标题字号、语义 icon、编号步骤和双击标签说明纳入 `usage_tips_qa.sh`。
 - [ ] 用户视觉体验验收。
 - [ ] 通过验收后再决定是否冻结、打包、创建正式 release tag。
 
diff --git a/src/Docs/Requirements/2026-06-24-app-grid-theme-system-worklog.md b/src/Docs/Requirements/2026-06-24-app-grid-theme-system-worklog.md
index c0cb414..8723f00 100644
--- a/src/Docs/Requirements/2026-06-24-app-grid-theme-system-worklog.md
+++ b/src/Docs/Requirements/2026-06-24-app-grid-theme-system-worklog.md
@@ -193,3 +193,64 @@
   - `src/build/TagLauncher-8.0.0-build20260624.1815.dmg`
 - SHA256:
   - `373355c87055680cb13e873bc5909abd84817d9ac2eaa98cc1ec2748be64bc80`
+
+### 2026-06-24 23:41 使用技巧横幅重设计
+
+- 用户确认最终方案:
+  - 布局选“2 分区式教学横幅”。
+  - 亮色主题使用浅玻璃配色。
+  - 深色/黑色主题使用同样布局,但切换到深色玻璃材质和白色文字。
+- 角色分工结论:
+  - 架构:继续使用原生 AppKit `AppGridUsageTipsNSView`,不引入 SwiftUI;tips 跟随 `renderedAppGridTheme`,编辑模式仍继承默认浅色主题 override。
+  - 代码审核:保留全宽透明 shield 和 local mouse monitor,避免点击穿透到底层 AppGrid;箭头只在命中区域触发翻页。
+  - QA:更新 `usage_tips_qa.sh`,覆盖分区式布局、主题配色、关闭按钮、全宽事件拦截、29 语种文案和 macOS 14 兼容。
+- 本轮实现:
+  - 使用技巧横幅底部横向占满可用宽度。
+  - 左侧独立标题面板显示灯泡图标、技巧编号和标题。
+  - 中央正文区域展示两行动作说明,保留长文本横向滚动能力。
+  - 右侧固定上一条/下一条按钮,页点移动到箭头下方。
+  - 右上角新增弱视觉关闭按钮;默认半透明,hover/press 更清晰;点击后写入 `hideUsageTips`。
+  - `updateColors()` 按 `AppGridTheme.usesDarkGlass` 切换浅玻璃/深玻璃 token。
+  - 29 个语种新增 `usageTips.close`。
+- QA:
+  - `bash Scripts/usage_tips_qa.sh`:PASS。
+  - `bash Scripts/macos14_availability_typecheck_qa.sh`:PASS。
+  - `git diff --check`:PASS。
+  - `bash build.sh`:PASS,生成 `src/build/TagLauncher.app`。
+  - `bash Scripts/macos14_build_metadata_qa.sh`:PASS,`LSMinimumSystemVersion=14.0`,`minos=14.0`,`arches=arm64`。
+  - `codesign --verify --deep --strict --verbose=2 build/TagLauncher.app`:PASS。
+  - 构建产物版本:`8.0.2 (20260625.1322)`。
+  - `bash Scripts/theme_settings_qa.sh`:PASS。
+  - `bash Scripts/tag_navigation_hover_scroll_qa.sh`:PASS。
+  - `bash Scripts/macos14_availability_typecheck_qa.sh`:PASS。
+  - `APP_BUILD=20260624.2331 bash build.sh`:PASS。
+
+### 2026-06-25 使用技巧横幅 26.0625-2 视觉优化
+
+- 需求来源:`canvas/26.06-UI调整.excalidraw` frame `26.0625-2`。
+- 用户标注问题:
+  - 标题区空间充裕但排版仍显拥挤。
+  - 标题字号偏小,需要适当放大并加粗,同时不能撑破 29 语种矩形空间。
+  - 标题字色需要按主题优化,必须保持清晰可读。
+  - 标题前 icon 不应固定为单一灯泡,需要按技巧类型变化。
+  - 正文超过一行时应采用 ordered list 样式。
+  - 第 1 条标题改为“编辑标签”,正文补充“打开设置切换到标签编辑”和“在标签列表双击标签快速进入标签编辑页”。
+  - 以上文案修改覆盖 29 个语种。
+- 本轮实现:
+  - `AppGridUsageTipsNSView` 标题字号从 22 semibold 调整为 25 bold。
+  - 左侧标题面板宽度从 300-380pt 扩展为 350-460pt,降低长语种标题换行/挤压风险。
+  - 标题和 icon 改为按 `AppGridTheme` 取高对比 accent;深蓝/黑色主题继续使用白字优先。
+  - 按 tip id 切换语义 SF Symbol:编辑标签、套用标签、移动、复制、移除、排序、备注等不再共用灯泡 icon。
+  - 正文 formatter 保留分隔符转换规则,并在多段内容时自动渲染为 `1.` / `2.` 编号步骤。
+  - 第 1 条使用技巧 29 个语种 title/detail 已同步更新。
+  - `Scripts/usage_tips_qa.sh` 增加标题字号、标题面板宽度、语义 icon、主题 accent、ordered list 和第 1 条双击标签说明检查。
+- QA:
+  - `bash Scripts/usage_tips_qa.sh`:PASS。
+  - `bash Scripts/macos14_build_metadata_qa.sh`:PASS,`LSMinimumSystemVersion=14.0`,`minos=14.0`,`arches=arm64`。
+  - `bash Scripts/appgrid_startup_loading_qa.sh`:PASS。
+  - `bash Scripts/app_ordering_data_qa.sh`:PASS。
+  - `bash Scripts/apple_default_note_policy_qa.sh`:PASS。
+  - `bash Scripts/apple_default_apps_resource_qa.sh`:PASS。
+  - `bash Scripts/quick_search_app_name_qa.sh`:SKIP,本机没有 `/Applications/贝锐向日葵被控.app` fixture。
+  - `bash Scripts/window_logic_qa.sh`:未作为本轮通过项;脚本在 Dock tile 去重检查处失败,退出后无 TagLauncher 进程残留,判断为 GUI/Dock 可访问性环境或既有 QA 基础设施问题,和本轮使用技巧横幅代码路径无直接交集。
+- 版本推进到:`8.0.2 / 20260624.2341`。
diff --git a/src/Scripts/usage_tips_qa.sh b/src/Scripts/usage_tips_qa.sh
index e663d6f..25008cb 100755
--- a/src/Scripts/usage_tips_qa.sh
+++ b/src/Scripts/usage_tips_qa.sh
@@ -7,6 +7,7 @@
 APPTAG_APP_SWIFT="$ROOT_DIR/Apptag/ApptagApp.swift"
 PREFERENCES_VIEW_SWIFT="$ROOT_DIR/Apptag/PreferencesView.swift"
 APP_DEFAULTS_SWIFT="$ROOT_DIR/Apptag/AppDefaults.swift"
+APP_GRID_THEME_SWIFT="$ROOT_DIR/Apptag/AppGridTheme.swift"
 LOCALIZATION_DIR="$ROOT_DIR/Apptag/Localization"
 
 fail() {
@@ -19,9 +20,10 @@
 [[ -f "$APPTAG_APP_SWIFT" ]] || fail "missing ApptagApp.swift"
 [[ -f "$PREFERENCES_VIEW_SWIFT" ]] || fail "missing PreferencesView.swift"
 [[ -f "$APP_DEFAULTS_SWIFT" ]] || fail "missing AppDefaults.swift"
+[[ -f "$APP_GRID_THEME_SWIFT" ]] || fail "missing AppGridTheme.swift"
 [[ -d "$LOCALIZATION_DIR" ]] || fail "missing Localization directory"
 
-python3 - "$CONTENT_VIEW_SWIFT" "$APP_GRID_SWIFT" "$APPTAG_APP_SWIFT" "$PREFERENCES_VIEW_SWIFT" "$APP_DEFAULTS_SWIFT" "$LOCALIZATION_DIR" <<'PY'
+python3 - "$CONTENT_VIEW_SWIFT" "$APP_GRID_SWIFT" "$APPTAG_APP_SWIFT" "$PREFERENCES_VIEW_SWIFT" "$APP_DEFAULTS_SWIFT" "$APP_GRID_THEME_SWIFT" "$LOCALIZATION_DIR" <<'PY'
 import json
 import pathlib
 import re
@@ -32,17 +34,8 @@
 apptag_app = pathlib.Path(sys.argv[3]).read_text(encoding="utf-8")
 preferences = pathlib.Path(sys.argv[4]).read_text(encoding="utf-8")
 defaults = pathlib.Path(sys.argv[5]).read_text(encoding="utf-8")
-localization_dir = pathlib.Path(sys.argv[6])
-
-required_keys = [
-    "settings.hideUsageTips",
-    "settings.hideUsageTipsDesc",
-    "usageTips.previous",
-    "usageTips.next",
-]
-for index in range(1, 9):
-    required_keys.append(f"usageTips.tip{index}.title")
-    required_keys.append(f"usageTips.tip{index}.detail")
+theme = pathlib.Path(sys.argv[6]).read_text(encoding="utf-8")
+localization_dir = pathlib.Path(sys.argv[7])
 
 
 def fail(message: str) -> None:
@@ -55,6 +48,17 @@
         fail(message)
 
 
+required_keys = [
+    "settings.hideUsageTips",
+    "settings.hideUsageTipsDesc",
+    "usageTips.previous",
+    "usageTips.next",
+    "usageTips.close",
+]
+for index in range(1, 9):
+    required_keys.append(f"usageTips.tip{index}.title")
+    required_keys.append(f"usageTips.tip{index}.detail")
+
 require(
     r"static\s+let\s+hideUsageTips\s*=\s*false",
     defaults,
@@ -65,121 +69,34 @@
     defaults,
     "AppDefaults.register must register hideUsageTips",
 )
-require(
-    r'@AppStorage\("hideUsageTips"\)\s+private\s+var\s+hideUsageTips\s*=\s*AppDefaults\.hideUsageTips',
-    content_view,
-    "ContentView must observe hideUsageTips with AppStorage",
-)
-require(
-    r'@AppStorage\("hideUsageTips"\)\s+private\s+var\s+hideUsageTips\s*=\s*AppDefaults\.hideUsageTips',
-    preferences,
-    "PreferencesView must persist hideUsageTips with AppStorage",
-)
+for source_name, source in {
+    "ContentView.swift": content_view,
+    "PreferencesView.swift": preferences,
+}.items():
+    require(
+        r'@AppStorage\("hideUsageTips"\)\s+private\s+var\s+hideUsageTips\s*=\s*AppDefaults\.hideUsageTips',
+        source,
+        f"{source_name} must persist hideUsageTips with AppStorage",
+    )
+
 require(
     r'Toggle\s*\(\s*tr\("settings\.hideUsageTips"\)\s*,\s*isOn\s*:\s*\$hideUsageTips\s*\)',
     preferences,
     "General settings must expose a Hide usage tips toggle",
 )
+
 require(
     r"\bprivate\s+final\s+class\s+AppGridUsageTipsNSView\s*:\s*NSView\b",
     app_grid,
     "Usage tips must be implemented as a native AppKit NSView",
 )
+if re.search(r"AppGridUsageTipsBar\s*:\s*View|usageTipNavigationButton|Text\s*\(\s*tr\s*\(\s*tip\.", content_view):
+    fail("usage tips must not be rendered by SwiftUI in ContentView")
+
 require(
-    r"enum\s+AppGridUsageTipsMetrics\s*\{(?P<body>.*?)static\s+let\s+barHeight\s*:\s*CGFloat\s*=\s*136(?P<body2>.*?)static\s+let\s+reservedHeight\s*:\s*CGFloat\s*=\s*176",
+    r"enum\s+AppGridUsageTipsMetrics\s*\{(?P<body>.*?)static\s+let\s+barHeight\s*:\s*CGFloat\s*=\s*154(?P<body2>.*?)static\s+let\s+reservedHeight\s*:\s*CGFloat\s*=\s*194",
     app_grid,
-    "Usage tips overlay must reserve a design-reviewed multi-line bottom space for the native HUD bar",
-)
-require(
-    r"NSVisualEffectView\s*\(",
-    app_grid,
-    "Usage tips must use a native macOS visual effect surface instead of split black/white blocks",
-)
-require(
-    r'systemSymbolName\s*:\s*"lightbulb\.fill"',
-    app_grid,
-    "Usage tips HUD must include a compact native guidance icon",
-)
-require(
-    r"preferredWidth\s*\(\s*maxAvailableWidth\s*:\s*CGFloat\s*\)",
-    app_grid,
-    "Usage tips HUD must be content-adaptive instead of stretched across the full AppGrid",
-)
-if re.search(r"static\s+let\s+maxWidth\s*:\s*CGFloat", app_grid):
-    fail("Usage tips HUD must not use a hard maximum width that truncates localized titles")
-require(
-    r"let\s+titleWidth\s*=\s*ceil\s*\(\s*titleLabel\.attributedStringValue\.size\(\)\.width\s*\)",
-    app_grid,
-    "Usage tips layout must measure the localized title width instead of using a fixed title column",
-)
-require(
-    r"configureLabel\s*\(\s*titleLabel\s*,\s*font\s*:\s*titleFont\s*,\s*lineBreakMode\s*:\s*\.byClipping\s*\)",
-    app_grid,
-    "Usage tip titles must not use tail truncation ellipses",
-)
-require(
-    r"let\s+titleWidth\s*=\s*availableTextWidth",
-    app_grid,
-    "Usage tip title label must span the text column so centered titles are visually centered",
-)
-require(
-    r"configureLabel\s*\(_\s+label\s*:\s*NSTextField,\s*font\s*:\s*NSFont,\s*lineBreakMode\s*:\s*NSLineBreakMode\s*\)\s*\{(?P<body>.*?)label\.alignment\s*=\s*\.center",
-    app_grid,
-    "Usage tip title labels must be horizontally centered",
-)
-if re.search(r"configureLabel\s*\(\s*titleLabel\s*,\s*font\s*:\s*titleFont\s*,\s*lineBreakMode\s*:\s*\.byTruncatingTail\s*\)", app_grid):
-    fail("Usage tip titles must not be configured with byTruncatingTail")
-require(
-    r"relayoutUsageTipsAfterContentChange\s*\(\s*\)",
-    app_grid,
-    "Usage tips must ask the host to recompute width after localized text changes",
-)
-require(
-    r"controlZoneWidth\s*:\s*CGFloat\s*=\s*176",
-    app_grid,
-    "Usage tips layout must reserve a fixed right-side control zone",
-)
-require(
-    r"availableTextWidth\s*=\s*max\s*\(\s*1\s*,\s*textRight\s*-\s*titleX\s*\)",
-    app_grid,
-    "Usage tips layout must allocate text from actual available AppGrid width",
-)
-require(
-    r"dotsView\.frame\s*=\s*NSRect\s*\((?P<body>.*?)x\s*:\s*buttonsGroupMinX\s*\+\s*\(buttonsWidth\s*-\s*dotsWidth\)\s*/\s*2(?P<body2>.*?)y\s*:\s*previousButton\.frame\.maxY\s*\+\s*buttonDotsGap",
-    app_grid,
-    "Usage tips page dots must sit centered below the previous/next arrow buttons",
-)
-require(
-    r"detailRight\s*=\s*textRight",
-    app_grid,
-    "Usage tips detail text must stop before the fixed right-side arrow control zone",
-)
-require(
-    r"configureDetailLabel\s*\(\s*\)(?P<body>.*?)detailLabel\.lineBreakMode\s*=\s*\.byWordWrapping",
-    app_grid,
-    "Usage tip detail text must wrap naturally inside the reserved text column",
-)
-require(
-    r"detailLabel\.maximumNumberOfLines\s*=\s*2",
-    app_grid,
-    "Usage tip detail text must use the design-reviewed two-line body layout",
-)
-require(
-    r"return\s+min\s*\(\s*maxAvailableWidth\s*,\s*preferredWidth\s*\)",
-    app_grid,
-    "Usage tips preferred width must expand up to available AppGrid width for long localizations",
-)
-require(
-    r"detailScrollView\.hasHorizontalScroller\s*=\s*true",
-    app_grid,
-    "Long localized tip text must be horizontally scrollable in the native AppKit bar",
-)
-if len(re.findall(r"NSFont\.systemFont\s*\(\s*ofSize\s*:\s*24\s*,\s*weight\s*:\s*\.(?:semibold|regular)\s*\)", app_grid)) < 2:
-    fail("Usage tip title/detail fonts must use readable 24pt native HUD typography")
-require(
-    r"override\s+func\s+scrollWheel\s*\(\s*with\s+event\s*:\s*NSEvent\s*\)\s*\{\s*detailScrollView\.scrollWheel\s*\(\s*with\s*:\s*event\s*\)",
-    app_grid,
-    "Usage tips must route wheel events to the native horizontal text scroller",
+    "usage tips must reserve the new design-reviewed full-width teaching banner height",
 )
 require(
     r"bottomContentPadding\s*:\s*shouldShowUsageTips\s*\?\s*AppGridUsageTipsMetrics\.reservedHeight\s*:\s*0",
@@ -189,257 +106,222 @@
 require(
     r"usageTipsVisible\s*:\s*shouldShowUsageTips",
     content_view,
-    "ContentView must pass visibility into the native AppKit usage tips bar",
+    "ContentView must pass usage tip visibility into AppGrid",
 )
 require(
     r"selectedUsageTipIndex\s*:\s*\$selectedUsageTipIndex",
     content_view,
-    "ContentView must bind usage tip selection to the native AppKit bar",
+    "ContentView must bind usage tip selection to AppGrid",
 )
 require(
-    r"override\s+func\s+hitTest\s*\(\s*_\s+point\s*:\s*NSPoint\s*\)\s*->\s*NSView\?\s*\{(?P<body>.*?)bounds\.contains\s*\(\s*point\s*\)",
+    r"onHideUsageTips\s*:\s*\{\s*hideUsageTips\s*=\s*true\s*\}",
+    content_view,
+    "closing the native usage tips banner must persist hideUsageTips",
+)
+require(
+    r"let\s+onHideUsageTips\s*:\s*\(\)\s*->\s*Void",
     app_grid,
-    "Native usage tips bar must own hit testing so clicks do not fall through to AppGrid",
+    "AppGridCollectionView must accept an onHideUsageTips callback",
 )
 require(
-    r"override\s+func\s+hitTest\s*\(\s*_\s+point\s*:\s*NSPoint\s*\)\s*->\s*NSView\?\s*\{(?P<body>.*?)usageTipsView\.frame\.contains\s*\(\s*point\s*\)",
+    r"closeButton\.action\s*=\s*\{\s*\[weak\s+self\]\s+in\s+self\?\.hideUsageTips\(\)\s*\}",
     app_grid,
-    "AppGrid host must prioritize the native usage tips hit-test region above the collection view",
+    "close button must call the native hideUsageTips handler",
 )
 require(
+    r"coordinator\?\.onHideUsageTips\(\)",
+    app_grid,
+    "native hideUsageTips must call back to persisted SwiftUI state",
+)
+
+require(
+    r"private\s+let\s+titlePanelView\s*=\s*NSView\(\)",
+    app_grid,
+    "usage tips must use a separated title panel",
+)
+require(
+    r"private\s+let\s+titleFont\s*=\s*NSFont\.systemFont\s*\(\s*ofSize\s*:\s*25\s*,\s*weight\s*:\s*\.bold\s*\)",
+    app_grid,
+    "usage tips title must use the design-reviewed larger bold title font",
+)
+require(
+    r"titlePanelWidth\s*=\s*min\s*\((?P<body>.*?)max\s*\(\s*350\s*,\s*visualFrame\.width\s*\*\s*0\.24\s*\)(?P<body2>.*?)min\s*\(\s*460\s*,\s*visualFrame\.width\s*\*\s*0\.36\s*\)",
+    app_grid,
+    "usage tips title panel must be wide enough for 29-language titles",
+)
+require(
+    r"private\s+let\s+closeButton\s*=\s*AppGridUsageTipIconButton\s*\(\s*systemImage\s*:\s*\"xmark\"\s*\)",
+    app_grid,
+    "usage tips must provide a low-emphasis native close button",
+)
+require(
+    r"controlZoneWidth\s*:\s*CGFloat\s*=\s*min\s*\(\s*148\s*,\s*max\s*\(\s*128\s*,\s*visualFrame\.width\s*\*\s*0\.08\s*\)\s*\)",
+    app_grid,
+    "usage tips must reserve a stable 128-148pt right-side control zone",
+)
+require(
+    r"func\s+preferredWidth\s*\(\s*maxAvailableWidth\s*:\s*CGFloat\s*\)\s*->\s*CGFloat\s*\{\s*max\s*\(\s*1\s*,\s*maxAvailableWidth\s*\)\s*\}",
+    app_grid,
+    "usage tips visual banner must occupy the available bottom width",
+)
+require(
+    r"dotsView\.frame\s*=\s*NSRect\s*\((?P<body>.*?)x\s*:\s*buttonsGroupMinX\s*\+\s*\(buttonsWidth\s*-\s*dotsWidth\)\s*/\s*2(?P<body2>.*?)y\s*:\s*previousButton\.frame\.maxY\s*\+\s*buttonDotsGap",
+    app_grid,
+    "page dots must be centered below the previous/next arrows",
+)
+require(
+    r"detailScrollView\.frame\s*=\s*NSRect\s*\((?P<body>.*?)x\s*:\s*detailX(?P<body2>.*?)width\s*:\s*max\s*\(\s*1\s*,\s*textRight\s*-\s*detailX\s*\)",
+    app_grid,
+    "detail text must live in the middle reading area and stop before controls",
+)
+require(
+    r"detailLabel\.lineBreakMode\s*=\s*\.byWordWrapping",
+    app_grid,
+    "detail text must wrap inside the two-line teaching banner",
+)
+require(
+    r"detailLabel\.maximumNumberOfLines\s*=\s*2",
+    app_grid,
+    "detail text must keep the design-reviewed two-line layout",
+)
+require(
+    r"detailLabel\.cell\s*=\s*AppGridCenteredMultilineTextFieldCell\s*\(\s*textCell\s*:\s*\"\"\s*\)",
+    app_grid,
+    "detail text must use a vertically centered multiline cell",
+)
+require(
+    r"paragraphStyle\.minimumLineHeight\s*=\s*31(?P<body>.*?)paragraphStyle\.maximumLineHeight\s*=\s*34(?P<body2>.*?)paragraphStyle\.lineSpacing\s*=\s*6",
+    app_grid,
+    "detail text must use the design-reviewed line height and spacing",
+)
+require(
+    r"detailScrollView\.hasHorizontalScroller\s*=\s*true",
+    app_grid,
+    "long localized tip text must remain horizontally scrollable",
+)
+require(
+    r"formattedTipDetail\s*\(_\s+text\s*:\s*String\s*\)",
+    app_grid,
+    "usage tips detail text must normalize separators for display",
+)
+require(
+    r"orderedTipDetail\s*\(_\s+text\s*:\s*String\s*\)(?P<body>.*?)map\s*\{\s*\"\\\(\$0\.offset\s*\+\s*1\)\.\s*\\\(\$0\.element\)\"\s*\}",
+    app_grid,
+    "multi-line usage tips detail must be rendered as an ordered list",
+)
+require(
+    r"replacingOccurrences\s*\(\s*of\s*:\s*\"\\\\s\*\(\?:-->\|->\|>\|→\|>\|,\)\\\\s\*\"\s*,\s*with\s*:\s*\"\\n\"",
+    app_grid,
+    "usage tips detail text must turn separators into hard line breaks",
+)
+require(
+    r"updateTipIcon\s*\(\s*id\s*:\s*tip\.id\s*\)",
+    app_grid,
+    "usage tips must update the leading icon for each tip",
+)
+require(
+    r"private\s+func\s+updateTipIcon\s*\(\s*id\s*:\s*Int\s*\)(?P<body>.*?)case\s+1:(?P<body2>.*?)tag\.fill(?P<body3>.*?)case\s+8:(?P<body4>.*?)note\.text",
+    app_grid,
+    "usage tips must vary the leading icon by tip type",
+)
+
+require(
+    r"private\s+func\s+updateColors\(\)(?P<body>.*?)coordinator\?\.appGridTheme\.usesDarkGlass\s*==\s*true",
+    app_grid,
+    "usage tips colors must be driven by the current AppGrid theme",
+)
+for token in [
+    r"backgroundView\.material\s*=\s*usesDarkGlass\s*\?\s*\.underWindowBackground\s*:\s*\.popover",
+    r"backgroundView\.appearance\s*=\s*usesDarkGlass\s*\?\s*NSAppearance\s*\(\s*named\s*:\s*\.darkAqua\s*\)\s*:\s*nil",
+    r"titlePanelView\.layer\?\.backgroundColor",
+    r"usageTipAccentColor\s*\(\s*for\s*:\s*theme\s*\)",
+    r"titleLabel\.textColor\s*=\s*usesDarkGlass",
+    r"detailLabel\.textColor\s*=\s*usesDarkGlass",
+    r"closeButton\.hoveredTintColor\s*=\s*usesDarkGlass",
+    r"dotsView\.selectedColor\s*=\s*usesDarkGlass",
+]:
+    require(token, app_grid, f"usage tips theme token missing: {token}")
+require(
+    r"private\s+func\s+usageTipAccentColor\s*\(\s*for\s+theme\s*:\s*AppGridTheme\s*\)\s*->\s*NSColor(?P<body>.*?)case\s+\.pink:(?P<body2>.*?)case\s+\.colorful:",
+    app_grid,
+    "usage tips title color must provide per-theme readable accent colors",
+)
+
+require(
+    r"var\s+usesDarkGlass\s*:\s*Bool\s*\{(?P<body>.*?)case\s+\.deepBlue,\s*\.black:(?P<body2>.*?)return\s+true",
+    theme,
+    "only dark/deep-black themes should use the dark glass palette",
+)
+
+for token in [
     r"usageTipsEventRegion\s*\(\s*\)\s*->\s*NSRect",
-    app_grid,
-    "AppGrid host must reserve a full bottom event region for tips so clicks cannot pass through",
-)
-require(
-    r"private\s+final\s+class\s+AppGridUsageTipsShieldView\s*:\s*NSView\b",
-    app_grid,
-    "AppGrid must use a native transparent shield view for the full bottom tips event region",
-)
-require(
     r"usageTipsShieldView\.frame\s*=\s*usageTipsEventRegion\s*\(\s*\)",
-    app_grid,
-    "Usage tips shield must cover the full bottom event region, not just the visible HUD",
-)
-require(
     r"usageTipsView\.frame\s*=\s*usageTipsEventRegion\s*\(\s*\)",
-    app_grid,
-    "Usage tips HUD hit-test view itself must cover the full bottom event region",
-)
-require(
-    r"configureVisualLayout\s*\(\s*width\s*:\s*CGFloat\s*,\s*height\s*:\s*CGFloat\s*,\s*bottomMargin\s*:\s*CGFloat\s*\)",
-    app_grid,
-    "Usage tips must separate full hit-test coverage from the centered visual HUD frame",
-)
-require(
-    r"currentVisualFrame\s*\(\s*\)\s*->\s*NSRect",
-    app_grid,
-    "Usage tips must compute a centered visual frame inside the full bottom hit-test region",
-)
-require(
     r"addSubview\s*\(\s*usageTipsShieldView\s*,\s*positioned\s*:\s*\.above\s*,\s*relativeTo\s*:\s*scrollView\s*\)",
-    app_grid,
-    "Usage tips shield must sit above the AppGrid collection scroll view",
-)
-require(
     r"addSubview\s*\(\s*usageTipsView\s*,\s*positioned\s*:\s*\.above\s*,\s*relativeTo\s*:\s*usageTipsShieldView\s*\)",
-    app_grid,
-    "Visible usage tips HUD must sit above the transparent event shield",
-)
-require(
     r"claimUsageTipsEventRegion\s*\(\s*\)",
-    app_grid,
-    "AppGrid host must claim bottom tips events and suppress lower bubbles",
-)
-require(
     r"handleUsageTipsMouseDown\s*\(\s*_\s+event\s*:\s*NSEvent\s*\)\s*->\s*Bool",
-    app_grid,
-    "AppGrid host must expose a native AppKit event router for usage tips clicks",
-)
+]:
+    require(token, app_grid, f"usage tips click-through protection missing: {token}")
 require(
     r"addLocalMonitorForEvents\s*\(\s*matching\s*:\s*\[(?P<body>.*?)\.leftMouseDown(?P<body2>.*?)\.leftMouseUp(?P<body3>.*?)\.rightMouseDown(?P<body4>.*?)\.otherMouseUp",
     app_grid,
     "AppGrid host must intercept usage tips mouseDown/mouseUp events before NSCollectionView app items receive them",
 )
 require(
-    r"\.leftMouseUp(?P<body>.*?)\.rightMouseUp(?P<body2>.*?)\.otherMouseUp",
-    app_grid,
-    "AppGrid host must also swallow mouseUp events in the usage tips region so app icons below cannot open on release",
-)
-require(
     r"triggerButtons\s*:\s*event\.type\s*==\s*\.leftMouseDown",
     app_grid,
-    "Usage tips monitor must only trigger paging on left mouseDown, not on mouseUp or right/other clicks",
+    "usage tips monitor must only trigger page changes on left mouseDown",
 )
 require(
-    r"usageTipsHostPoint\s*\(\s*for\s+event\s*:\s*NSEvent\s*\)\s*->\s*NSPoint",
+    r"func\s+handleMouseEventFromHost\s*\(\s*_\s+event\s*:\s*NSEvent\s*,\s*triggerButtons\s*:\s*Bool\s*\)\s*->\s*Bool\s*\{(?P<body>.*?)if\s+triggerButtons,\s*routeButtonClickIfNeeded\s*\(\s*event\s*\)(?P<body2>.*?)claimInteractionFocus\(\)(?P<body3>.*?)return\s+true",
     app_grid,
-    "Usage tips mouse monitor must resolve the event position through the AppGrid host",
+    "host-routed tips clicks must consume the event and only trigger buttons on hit regions",
 )
-require(
-    r"window\.convertPoint\s*\(\s*fromScreen\s*:\s*NSEvent\.mouseLocation\s*\)",
-    app_grid,
-    "Usage tips hit testing must fall back to current screen mouse location when event.window is unreliable",
-)
-require(
-    r"self\.handleUsageTipsMouseEvent\s*\(\s*event\s*,(?P<body>.*?)triggerButtons\s*:\s*event\.type\s*==\s*\.leftMouseDown(?P<body2>.*?)return\s+nil",
-    app_grid,
-    "Usage tips mouse monitor must swallow handled bottom-tip events so app icons below cannot open",
-)
-require(
-    r"removeUsageTipsMouseMonitor\s*\(\s*\)",
-    app_grid,
-    "Usage tips mouse monitor must be removed when the AppGrid host leaves its window",
-)
-require(
-    r"func\s+handleMouseEventFromHost\s*\(\s*_\s+event\s*:\s*NSEvent\s*,\s*triggerButtons\s*:\s*Bool\s*\)\s*->\s*Bool\s*\{(?P<body>.*?)if\s+triggerButtons\s*,\s*routeButtonClickIfNeeded\s*\(\s*event\s*\)(?P<body2>.*?)claimInteractionFocus\s*\(\s*\)(?P<body3>.*?)return\s+true",
-    app_grid,
-    "Usage tips host-routed clicks must only trigger page changes on arrow hit regions and otherwise only consume the event",
-)
-if re.search(r"handleMouseEventFromHost\s*\(\s*_\s+event\s*:\s*NSEvent\s*,\s*triggerButtons\s*:\s*Bool\s*\)\s*->\s*Bool\s*\{(?P<body>.*?)target\.mouseDown", app_grid, re.S):
-    fail("Usage tips host-routed clicks must not redispatch arbitrary mouseDown events that can pierce to app icons")
-require(
-    r"override\s+func\s+mouseDown\s*\(\s*with\s+event\s*:\s*NSEvent\s*\)",
-    app_grid,
-    "Native usage tips bar must swallow mouseDown events instead of letting clicks close the grid",
-)
-require(
+if re.search(r"handleMouseEventFromHost\s*\(\s*_\s+event\s*:\s*NSEvent,\s*triggerButtons\s*:\s*Bool\s*\).*?target\.mouseDown", app_grid, re.S):
+    fail("usage tips host-routed clicks must not redispatch arbitrary mouseDown events")
+
+for token in [
     r"private\s+final\s+class\s+AppGridUsageTipIconButton\s*:\s*NSView\b",
-    app_grid,
-    "Usage tip navigation controls must be native AppKit hit-testable views",
-)
-require(
-    r"private\s+final\s+class\s+AppGridDecorativeImageView\s*:\s*NSImageView\b(?P<body>.*?)override\s+func\s+hitTest\s*\(\s*_\s+point\s*:\s*NSPoint\s*\)\s*->\s*NSView\?\s*\{\s*nil\s*\}",
-    app_grid,
-    "Usage tip icon images must not steal hit testing from their parent AppKit controls",
-)
-require(
-    r"private\s+final\s+class\s+AppGridUsageTipIconButton\s*:\s*NSView\b(?P<body>.*?)acceptsFirstMouse\s*\(\s*for\s+event\s*:\s*NSEvent\?\s*\)\s*->\s*Bool\s*\{\s*true\s*\}",
-    app_grid,
-    "Usage tip navigation controls must accept first mouse clicks in the floating overlay",
-)
-require(
+    r"override\s+func\s+acceptsFirstMouse\s*\(\s*for\s+event\s*:\s*NSEvent\?\s*\)\s*->\s*Bool\s*\{\s*true\s*\}",
     r"override\s+func\s+isAccessibilityElement\s*\(\s*\)\s*->\s*Bool\s*\{\s*true\s*\}",
-    app_grid,
-    "Usage tip navigation controls must be exposed as accessibility elements",
-)
-require(
     r"override\s+func\s+accessibilityRole\s*\(\s*\)\s*->\s*NSAccessibility\.Role\?\s*\{\s*\.button\s*\}",
-    app_grid,
-    "Usage tip navigation controls must expose the AX button role",
-)
-require(
     r"override\s+func\s+accessibilityPerformPress\s*\(\s*\)\s*->\s*Bool\s*\{(?P<body>.*?)action\?\(\)(?P<body2>.*?)return\s+true",
-    app_grid,
-    "Usage tip navigation controls must support AX press actions",
-)
-require(
-    r"override\s+var\s+mouseDownCanMoveWindow\s*:\s*Bool\s*\{\s*false\s*\}",
-    app_grid,
-    "Usage tips hit-test views must not let mouseDown move or dismiss the overlay window",
-)
-require(
-    r"func\s+selectUsageTip\s*\(\s*offset\s*:\s*Int\s*\)",
-    app_grid,
-    "Usage tip navigation must update the selected tip index from AppKit",
-)
+    r"var\s+hoveredTintColor\s*:\s*NSColor\?",
+    r"var\s+pressedTintColor\s*:\s*NSColor\?",
+    r"func\s+performPressFeedback\(\)",
+    r"private\s+func\s+updateIconTint\(\)",
+]:
+    require(token, app_grid, f"native usage tip control behavior missing: {token}")
 require(
     r"previousButton\.action\s*=\s*\{\s*\[weak\s+self\]\s+in\s+self\?\.selectUsageTip\s*\(\s*offset\s*:\s*-1\s*\)\s*\}",
     app_grid,
-    "Previous usage tip control must call the native selection handler",
+    "previous usage tip control must page backward",
 )
 require(
     r"nextButton\.action\s*=\s*\{\s*\[weak\s+self\]\s+in\s+self\?\.selectUsageTip\s*\(\s*offset\s*:\s*1\s*\)\s*\}",
     app_grid,
-    "Next usage tip control must call the native selection handler",
+    "next usage tip control must page forward",
 )
 require(
-    r"override\s+func\s+mouseDown\s*\(\s*with\s+event\s*:\s*NSEvent\s*\)\s*\{(?P<body>.*?)action\?\(\)",
+    r"routeButtonClickIfNeeded\s*\(_\s+event\s*:\s*NSEvent\s*\)(?P<body>.*?)nextButton\.frame\.insetBy(?P<body2>.*?)previousButton\.frame\.insetBy",
     app_grid,
-    "Native usage tip icon buttons must invoke their action directly on mouseDown",
+    "usage tip arrows must only respond inside their hit regions",
 )
 require(
-    r"onUsageTipIndexChange\s*\(\s*nextIndex\s*\)",
+    r"override\s+func\s+keyDown\s*\(\s*with\s+event\s*:\s*NSEvent\s*\)(?P<body>.*?)kVK_LeftArrow(?P<body2>.*?)kVK_RightArrow",
     app_grid,
-    "Native usage tip selection must publish the new index back to SwiftUI state",
-)
-require(
-    r"override\s+func\s+keyDown\s*\(\s*with\s+event\s*:\s*NSEvent\s*\)\s*\{(?P<body>.*?)kVK_LeftArrow(?P<body2>.*?)kVK_RightArrow",
-    app_grid,
-    "Focused native usage tips HUD must support left/right arrow key switching",
-)
-require(
-    r"formattedTipDetail\s*\(_\s+text\s*:\s*String\s*\)",
-    app_grid,
-    "Usage tips detail text must normalize separators for display",
-)
-require(
-    r"replacingOccurrences\s*\(\s*of\s*:\s*\"\\\\s\*\(\?:-->\|->\|>\|→\|>\|,\)\\\\s\*\"\s*,\s*with\s*:\s*\"\\n\"",
-    app_grid,
-    "Usage tips detail text must turn navigation markers and commas into hard line breaks",
-)
-require(
-    r"setUsageTipsBubbleDisabled\s*\(\s*inside\s*\)",
-    app_grid,
-    "Hovering usage tips must suppress lower AppGrid bubbles",
-)
-require(
-    r"onUsageTipsHoverChange\s*\(\s*inside\s*\)",
-    app_grid,
-    "Hovering usage tips must notify ContentView to clear already visible SwiftUI bubbles",
-)
-require(
-    r"shouldSwallowUsageTipsBackdropClick\s*\(\s*at\s+location\s*:\s*NSPoint\s*\)\s*->\s*Bool",
-    apptag_app,
-    "DismissibleHostingView must not treat bottom usage tips clicks as backdrop taps",
-)
-require(
-    r"routeUsageTipsMouseDownIfNeeded\s*\(\s*event\s*\)",
-    apptag_app,
-    "DismissibleHostingView must forward bottom usage tips clicks to native AppKit controls before swallowing",
-)
-require(
-    r"findAppGridCollectionHost\s*\(\s*in\s+view\s*:\s*NSView\s*\)\s*->\s*AppGridCollectionHostView\?",
-    apptag_app,
-    "DismissibleHostingView must locate the native AppGrid host for usage tips event forwarding",
-)
-require(
-    r"AppGridUsageTipsMetrics\.reservedHeight",
-    apptag_app,
-    "Backdrop usage tips guard must match the native AppGrid reserved height",
-)
-require(
-    r'UserDefaults\.standard\.bool\s*\(\s*forKey\s*:\s*"hideUsageTips"\s*\)',
-    apptag_app,
-    "Backdrop usage tips guard must respect the hide usage tips setting",
-)
-require(
-    r"private\s+func\s+handleUsageTipsHoverChange\s*\(\s*_\s+hovering\s*:\s*Bool\s*\)",
-    content_view,
-    "ContentView must clear existing app bubbles while the native usage tips bar is hovered",
-)
-if re.search(r"AppGridUsageTipsBar\s*:\s*View|usageTipNavigationButton|ScrollView\s*\(\s*\.horizontal\s*,\s*showsIndicators\s*:\s*textHovered", content_view):
-    fail("usage tips must not be implemented with SwiftUI views in ContentView")
-if re.search(r"Text\s*\(\s*tr\s*\(\s*tip\.(?:titleKey|detailKey)", content_view):
-    fail("usage tip title/detail rendering must not use SwiftUI Text")
-require(
-    r"let\s+bottomContentPadding\s*:\s*CGFloat",
-    app_grid,
-    "AppGridCollectionView must accept bottom content padding",
-)
-if '"bottom=\\(Int(bottomContentPadding.rounded()))"' not in app_grid:
-    fail("AppGrid layout signature must include bottom content padding")
-require(
-    r"\+\s*max\s*\(\s*0\s*,\s*bottomContentPadding\s*\)",
-    app_grid,
-    "AppGrid layout plans must add bottom content padding to content height",
+    "focused native usage tips HUD must support left/right arrow keys",
 )
 
-tip_ids = [int(value) for value in re.findall(r"AppGridUsageTip\s*\(\s*id\s*:\s*(\d+)", content_view)]
-if tip_ids != list(range(1, 9)):
-    fail(f"usage tip IDs must be exactly 1 through 8, got {tip_ids}")
-if "usageTips.tip0" in content_view:
-    fail("usage tips must not expose a zero-based tip number")
+for token in [
+    r"shouldSwallowUsageTipsBackdropClick\s*\(\s*at\s+location\s*:\s*NSPoint\s*\)\s*->\s*Bool",
+    r"routeUsageTipsMouseDownIfNeeded\s*\(\s*event\s*\)",
+    r"findAppGridCollectionHost\s*\(\s*in\s+view\s*:\s*NSView\s*\)\s*->\s*AppGridCollectionHostView\?",
+    r"AppGridUsageTipsMetrics\.reservedHeight",
+    r'UserDefaults\.standard\.bool\s*\(\s*forKey\s*:\s*"hideUsageTips"\s*\)',
+]:
+    require(token, apptag_app, f"App window backdrop usage-tips protection missing: {token}")
 
 for source_name, source in {
     "ContentView.swift": content_view,
@@ -450,6 +332,12 @@
         fail(f"{source_name} must not rely on macOS 15/26 availability for usage tips")
     if "scrollClipDisabled" in source:
         fail(f"{source_name} must avoid newer scrollClipDisabled behavior for macOS 14 compatibility")
+
+tip_ids = [int(value) for value in re.findall(r"AppGridUsageTip\s*\(\s*id\s*:\s*(\d+)", content_view)]
+if tip_ids != list(range(1, 9)):
+    fail(f"usage tip IDs must be exactly 1 through 8, got {tip_ids}")
+if "usageTips.tip0" in content_view:
+    fail("usage tips must not expose a zero-based tip number")
 
 json_files = sorted(localization_dir.glob("*.json"))
 if len(json_files) != 29:
@@ -467,6 +355,23 @@
         title = data[f"usageTips.tip{tip_index}.title"]
         if re.search(r"\s*[::]\s*$", title):
             fail(f"{path.name} usageTips.tip{tip_index}.title must not end with a colon")
+    tip1_title = data["usageTips.tip1.title"]
+    tip1_detail = data["usageTips.tip1.detail"]
+    if path.stem == "en":
+        if tip1_title != "Tip 1 - Edit tags":
+            fail("en.json usageTips.tip1.title must describe editing tags")
+        if "Double-click" not in tip1_detail or "tag list" not in tip1_detail:
+            fail("en.json usageTips.tip1.detail must mention double-clicking a tag in the tag list")
+    if path.stem == "zh-Hans":
+        if tip1_title != "技巧1-编辑标签":
+            fail("zh-Hans.json usageTips.tip1.title must be 编辑标签")
+        if "双击标签" not in tip1_detail or "标签列表" not in tip1_detail:
+            fail("zh-Hans.json usageTips.tip1.detail must mention double-clicking in the tag list")
+    if path.stem == "zh-Hant":
+        if tip1_title != "技巧1-編輯標籤":
+            fail("zh-Hant.json usageTips.tip1.title must be 編輯標籤")
+        if "雙擊標籤" not in tip1_detail or "標籤列表" not in tip1_detail:
+            fail("zh-Hant.json usageTips.tip1.detail must mention double-clicking in the tag list")
     if path.stem not in {"zh-Hans", "zh-Hant"}:
         for tip_index in range(1, 9):
             detail = data[f"usageTips.tip{tip_index}.detail"]
@@ -478,5 +383,5 @@
             if not first or not second:
                 fail(f"{path.name} usageTips.tip{tip_index}.detail must have text on both sides of the comma line break")
 
-print("PASS usage tips QA: overlay, hide setting, macOS 14-safe implementation, and 29-language localizations are present")
+print("PASS usage tips QA: native split teaching banner, theme-aware glass, click protection, close action, and 29-language localizations are present")
 PY

--
Gitblit v1.9.3