Ariver
2026-06-20 cba722f8a207982ade9e941eca3e6070e8705749
C1.source/Sources/Aligner/QuickSwitchRootView.swift
@@ -10,9 +10,25 @@
@MainActor
final class QuickSwitchRootView: NSView {
    private static let appShelfIndexSymbols = Array("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ").map(String.init)
    private static let windowIndexKeyPendingInterval: CFTimeInterval = 0.8
    private static let appShelfIndexKeyCodes: [String: UInt16] = [
        "1": 18, "2": 19, "3": 20, "4": 21, "5": 23,
        "6": 22, "7": 26, "8": 28, "9": 25, "0": 29,
        "A": 0, "B": 11, "C": 8, "D": 2, "E": 14, "F": 3,
        "G": 5, "H": 4, "I": 34, "J": 38, "K": 40, "L": 37,
        "M": 46, "N": 45, "O": 31, "P": 35, "Q": 12, "R": 15,
        "S": 1, "T": 17, "U": 32, "V": 9, "W": 13, "X": 7,
        "Y": 16, "Z": 6
    ]
    private let backdropBlurView = NSVisualEffectView()
    private let layerHostView = NSView()
    private let backgroundLayer = CAGradientLayer()
    private let darkAmbientLeftLayer = CAGradientLayer()
    private let darkAmbientCenterLayer = CAGradientLayer()
    private let darkAmbientRightLayer = CAGradientLayer()
    private let darkVignetteLayer = CAGradientLayer()
    private let safeAreaFillLayer = CALayer()
    private let glassLayer = CALayer()
    private let laneLayer = CALayer()
@@ -24,9 +40,10 @@
    private lazy var closeConfirmationLayers = makeCloseConfirmationLayers()
    private let closeFeedbackLayer = CALayer()
    private let closeFeedbackTextLayer = CATextLayer()
    private let settingsButtonLayer = CALayer()
    private let settingsButtonIconLayer = CALayer()
    private let settingsButtonFallbackTextLayer = CATextLayer()
    private let viewModeToggleButtonLayer = CALayer()
    private let viewModeToggleIconLayer = CALayer()
    private let themeToggleButtonLayer = CALayer()
    private let themeToggleIconLayer = CALayer()
    private let appIconProvider: any AppIconProviderProtocol = WorkspaceAppIconProvider()
    private let skeletonThumbnailProvider = NativeSkeletonThumbnailProvider(size: NSSize(width: 160, height: 96))
    private var screenshotSourcesByWindowID: [UInt32: ScreenshotResolutionSource] = [:]
@@ -52,7 +69,10 @@
    private var closeConfirmationRequired = true
    private var closeFeedback: CloseFeedback?
    private var closeFeedbackHideTask: Task<Void, Never>?
    private var isSettingsButtonHovered = false
    private var isViewModeToggleButtonHovered = false
    private var suppressViewModeToggleHoverPreview = false
    private var isThemeToggleButtonHovered = false
    private var suppressThemeToggleHoverPreview = false
    private var hoverTarget: HoverTarget = .none
    private var hoverTargetSource: HoverTargetSource = .event
    private var hoveredSpaceLaneID: UInt64?
@@ -60,6 +80,7 @@
    private var debugHoveredAppGroupIndex: Int?
    private var waterfallColumns: [WaterfallColumnLayers] = []
    private var waterfallViewMode: QuickSwitchWaterfallViewMode = .verticalColumns
    private var themePreference: ThemePreference = .light
    private var waterfallContentWidth: CGFloat = 0
    private var waterfallScrollOffset: CGFloat = 0
    private var waterfallColumnScrollOffsets: [Int: CGFloat] = [:]
@@ -67,8 +88,23 @@
    private var horizontalMasonryScrollOffset: CGFloat = 0
    private var currentSelection: QuickSwitchSelection?
    private var columnSelectionHistory: [Int: Int] = [:]
    private var keyboardFocusedAppGroupIndex: Int?
    private var keyboardFocusedWindowID: UInt32?
    private var keyboardCommandsApplied: [String] = []
    private var lastKeyboardCommand: String?
    private var lastKeyboardAppIndexCommand: String?
    private var appShelfIndexKeyDownCount = 0
    private var lastAppShelfIndexKeySymbol: String?
    private var windowIndexKeyPending: WindowIndexKeyPending?
    private var windowIndexKeyPendingClearTask: Task<Void, Never>?
    private var lastWindowIndexKeyCommand: String?
    private var lastWindowIndexKeyCommitCode: String?
    private var windowShortcutFilterPhase: WindowShortcutFilterPhase = .none
    private var lastWindowShortcutPreCommitFilterSnapshot: WindowShortcutPreCommitFilterSnapshot?
    private var lastBoundaryBounceAxis: String?
    private var lastBoundaryBounceDirection: String?
    private var boundaryBounceCount = 0
    private var selectionChangedByLastCommand: Bool?
    private var tabIgnoredCount = 0
    private var lastCommittedSelection: QuickSwitchSelection?
    private var lastCommitSource: QuickSwitchCommitSource?
@@ -95,7 +131,8 @@
    var onCommitSelection: ((QuickSwitchSelection, QuickSwitchCommitSource) -> Void)?
    var onSpaceLaneClick: ((UInt64, Int) -> Void)?
    var onBackgroundClick: (() -> Bool)?
    var onOpenSettings: (() -> Void)?
    var onToggleWaterfallViewMode: (() -> Void)?
    var onToggleTheme: (() -> Void)?
    var onRequestClose: ((QuickSwitchCloseRequest) -> Void)?
    var onCloseConfirmationDisabled: (() -> Void)?
@@ -218,6 +255,33 @@
    private enum HoverTargetSource: String {
        case event
        case debug
        case keyboard
    }
    private enum HorizontalKeyboardFocusKind: String {
        case app
        case window
    }
    private struct HorizontalKeyboardFocusNode {
        let kind: HorizontalKeyboardFocusKind
        let appGroupIndex: Int
        let windowIndex: Int?
        let windowID: UInt32?
        let spaceID: UInt64?
        let frame: CGRect
        let order: Int
        var center: CGPoint {
            CGPoint(x: frame.midX, y: frame.midY)
        }
        func matches(_ other: HorizontalKeyboardFocusNode) -> Bool {
            kind == other.kind
                && appGroupIndex == other.appGroupIndex
                && windowIndex == other.windowIndex
                && windowID == other.windowID
        }
    }
    private struct SpaceLaneSegmentLayers {
@@ -226,6 +290,7 @@
        let labelLayer: CATextLayer
        let countLayer: CATextLayer
        let appLayer: CATextLayer
        let splitAppLayers: [CATextLayer]
        let windowBlocks: [CALayer]
        let fullscreenMarkerLayer: CALayer?
    }
@@ -240,6 +305,7 @@
        let item: QuickSwitchAppShelfItemViewModel
        let containerLayer: CALayer
        let backgroundLayer: CALayer
        let indexLayer: CATextLayer
        let iconLayer: CALayer
        let labelLayer: CATextLayer
        let badgeLayer: CATextLayer
@@ -255,6 +321,7 @@
    private struct WaterfallColumnLayers {
        let column: QuickSwitchWaterfallColumnViewModel
        let containerLayer: CALayer
        let cardsClipLayer: CALayer
        let headerLayer: CALayer
        let appNameLayer: CATextLayer
        let countLayer: CATextLayer
@@ -272,6 +339,53 @@
        let stateLayer: CATextLayer
        let shineLayer: CAGradientLayer
        let closeButton: CloseButtonLayers
    }
    private struct WindowIndexKeyPending {
        let appSymbol: String
        let appGroupIndex: Int
        let startedAt: CFTimeInterval
    }
    private enum WindowShortcutFilterPhase: String {
        case none
        case appPending
        case targetPreCommit
        case invalid
        case expired
    }
    private enum WindowShortcutFilterState: String {
        case none
        case matched
        case dimmed
        case target
    }
    private struct WindowShortcutPreCommitFilterSnapshot {
        let targetCode: String
        let targetWindowID: UInt32
        let matchedWindowIDs: [UInt32]
        let dimmedWindowIDs: [UInt32]
        var reportDictionary: [String: Any] {
            [
                "phase": WindowShortcutFilterPhase.targetPreCommit.rawValue,
                "targetCode": targetCode,
                "targetWindowID": Int(targetWindowID),
                "matchedWindowIDs": matchedWindowIDs.map(Int.init),
                "dimmedWindowIDs": dimmedWindowIDs.map(Int.init)
            ]
        }
    }
    private struct WindowShortcutFilterReport {
        let active: Bool
        let phase: WindowShortcutFilterPhase
        let pendingAppGroupIndex: Int?
        let prefix: String?
        let matchedWindowIDs: [UInt32]
        let dimmedWindowIDs: [UInt32]
    }
    private struct ProjectionLayerSnapshot {
@@ -298,6 +412,10 @@
        static let normalGap: CGFloat = 16
        static let minGap: CGFloat = 8
        static let maxRows = 2
        static let indexFontSize: CGFloat = 16
        static let compressedIndexFontSize: CGFloat = 14
        static let indexWidth: CGFloat = 18
        static let indexHeight: CGFloat = 24
    }
    private enum RootLayoutMetrics {
@@ -306,6 +424,136 @@
        static let shelfGap: CGFloat = 16
        static let waterfallTopGap: CGFloat = 14
        static let bottomMargin: CGFloat = 34
    }
    private enum ViewModeToggleMetrics {
        static let hitSize: CGFloat = 34
        static let visualSize: CGFloat = 30
        static let cornerRadius: CGFloat = 9
        static let iconInset: CGFloat = 7
        static let glyphLineWidth: CGFloat = 1.65
        static let glyphCornerRadius: CGFloat = 2.1
        static let selectedShadowRadius: CGFloat = 10
        static let idleShadowRadius: CGFloat = 5
    }
    private enum ViewModeToggleVisualState: String {
        case horizontalAvailable = "H1"
        case horizontalSelected = "H2"
        case verticalAvailable = "Z1"
        case verticalSelected = "Z2"
    }
    private enum ThemeToggleMetrics {
        static let buttonGap: CGFloat = 10
    }
    private enum QuickSwitchResolvedTheme: String {
        case light
        case dark
    }
    private struct QuickSwitchThemePalette {
        let resolvedTheme: QuickSwitchResolvedTheme
        let backgroundGradientColors: [NSColor]
        let safeAreaFill: NSColor
        let displayGroupBackground: NSColor
        let spaceLanePrimaryText: NSColor
        let spaceLaneSecondaryText: NSColor
        let columnBackground: NSColor
        let columnBorder: NSColor
        let columnHeaderBackground: NSColor
        let columnHeaderHoverBackground: NSColor
        let columnHeaderSpaceFocusedBackground: NSColor
        let columnText: NSColor
        let columnSecondaryText: NSColor
        let cardBackground: NSColor
        let cardSelectedBackground: NSColor
        let cardText: NSColor
        let cardSecondaryText: NSColor
        let appShelfLabelText: NSColor
        let appShelfIndexText: NSColor
        let appShelfIndexHighlightedText: NSColor
        let appShelfBackgroundSelected: NSColor
        let appShelfBackgroundHovered: NSColor
        let appShelfBackgroundSpaceFocused: NSColor
        let closeFeedbackBackground: NSColor
        let closeFeedbackBorder: NSColor
        let closeFeedbackText: NSColor
        let shinePeak: NSColor
        static func palette(for theme: QuickSwitchResolvedTheme) -> QuickSwitchThemePalette {
            switch theme {
            case .light:
                return QuickSwitchThemePalette(
                    resolvedTheme: .light,
                    backgroundGradientColors: [
                        NSColor.windowBackgroundColor.withAlphaComponent(0.58),
                        NSColor.controlBackgroundColor.withAlphaComponent(0.48)
                    ],
                    safeAreaFill: .black,
                    displayGroupBackground: NSColor.windowBackgroundColor,
                    spaceLanePrimaryText: .labelColor,
                    spaceLaneSecondaryText: .secondaryLabelColor,
                    columnBackground: NSColor.controlBackgroundColor.withAlphaComponent(0.56),
                    columnBorder: NSColor.separatorColor.withAlphaComponent(0.24),
                    columnHeaderBackground: NSColor.controlBackgroundColor.withAlphaComponent(0.52),
                    columnHeaderHoverBackground: NSColor.systemGray.withAlphaComponent(0.22),
                    columnHeaderSpaceFocusedBackground: NSColor.systemFill.withAlphaComponent(0.10),
                    columnText: .labelColor,
                    columnSecondaryText: .secondaryLabelColor,
                    cardBackground: NSColor.windowBackgroundColor.withAlphaComponent(0.82),
                    cardSelectedBackground: NSColor.windowBackgroundColor.withAlphaComponent(0.94),
                    cardText: .labelColor,
                    cardSecondaryText: .secondaryLabelColor,
                    appShelfLabelText: .secondaryLabelColor,
                    appShelfIndexText: NSColor.labelColor.withAlphaComponent(0.48),
                    appShelfIndexHighlightedText: NSColor.labelColor.withAlphaComponent(0.58),
                    appShelfBackgroundSelected: NSColor.controlAccentColor.withAlphaComponent(0.14),
                    appShelfBackgroundHovered: NSColor.controlAccentColor.withAlphaComponent(0.13),
                    appShelfBackgroundSpaceFocused: NSColor.controlAccentColor.withAlphaComponent(0.14),
                    closeFeedbackBackground: NSColor.windowBackgroundColor.withAlphaComponent(0.94),
                    closeFeedbackBorder: NSColor.separatorColor.withAlphaComponent(0.28),
                    closeFeedbackText: NSColor.secondaryLabelColor.withAlphaComponent(0.96),
                    shinePeak: NSColor.white.withAlphaComponent(0.16)
                )
            case .dark:
                return QuickSwitchThemePalette(
                    resolvedTheme: .dark,
                    backgroundGradientColors: [
                        NSColor(calibratedRed: 5 / 255, green: 8 / 255, blue: 20 / 255, alpha: 1),
                        NSColor(calibratedRed: 15 / 255, green: 24 / 255, blue: 54 / 255, alpha: 1),
                        NSColor(calibratedRed: 3 / 255, green: 42 / 255, blue: 52 / 255, alpha: 1),
                        NSColor(calibratedRed: 2 / 255, green: 6 / 255, blue: 13 / 255, alpha: 1)
                    ],
                    safeAreaFill: .black,
                    displayGroupBackground: NSColor(calibratedRed: 14 / 255, green: 24 / 255, blue: 39 / 255, alpha: 0.72),
                    spaceLanePrimaryText: NSColor.white.withAlphaComponent(0.88),
                    spaceLaneSecondaryText: NSColor.white.withAlphaComponent(0.56),
                    columnBackground: NSColor(calibratedRed: 92 / 255, green: 97 / 255, blue: 111 / 255, alpha: 0.72),
                    columnBorder: NSColor.white.withAlphaComponent(0.20),
                    columnHeaderBackground: NSColor(calibratedRed: 104 / 255, green: 111 / 255, blue: 126 / 255, alpha: 0.70),
                    columnHeaderHoverBackground: NSColor(calibratedRed: 185 / 255, green: 190 / 255, blue: 205 / 255, alpha: 0.32),
                    columnHeaderSpaceFocusedBackground: NSColor(calibratedRed: 92 / 255, green: 171 / 255, blue: 255 / 255, alpha: 0.16),
                    columnText: NSColor.black.withAlphaComponent(0.82),
                    columnSecondaryText: NSColor.black.withAlphaComponent(0.46),
                    cardBackground: NSColor.white.withAlphaComponent(0.90),
                    cardSelectedBackground: NSColor.white.withAlphaComponent(0.96),
                    cardText: NSColor.black.withAlphaComponent(0.86),
                    cardSecondaryText: NSColor.black.withAlphaComponent(0.52),
                    appShelfLabelText: NSColor.white.withAlphaComponent(0.66),
                    appShelfIndexText: NSColor.white.withAlphaComponent(0.40),
                    appShelfIndexHighlightedText: NSColor.white.withAlphaComponent(0.58),
                    appShelfBackgroundSelected: NSColor(calibratedRed: 92 / 255, green: 171 / 255, blue: 255 / 255, alpha: 0.22),
                    appShelfBackgroundHovered: NSColor(calibratedRed: 92 / 255, green: 171 / 255, blue: 255 / 255, alpha: 0.18),
                    appShelfBackgroundSpaceFocused: NSColor(calibratedRed: 92 / 255, green: 171 / 255, blue: 255 / 255, alpha: 0.20),
                    closeFeedbackBackground: NSColor(calibratedRed: 36 / 255, green: 43 / 255, blue: 58 / 255, alpha: 0.96),
                    closeFeedbackBorder: NSColor.white.withAlphaComponent(0.18),
                    closeFeedbackText: NSColor.white.withAlphaComponent(0.76),
                    shinePeak: NSColor.white.withAlphaComponent(0.10)
                )
            }
        }
    }
    private enum WaterfallMetrics {
@@ -322,18 +570,18 @@
        static let cardGap: CGFloat = 12
        static let revealPadding: CGFloat = 24
        static let horizontalRevealPadding: CGFloat = 0
        static let selectedCardScale: CGFloat = 1.015
        static let selectedCardScaleOverflow: CGFloat = cardHeight * (selectedCardScale - 1) / 2
    }
    private enum HorizontalMasonryMetrics {
        static let horizontalPadding: CGFloat = 12
        static let verticalPadding: CGFloat = 0
        static let cardGap: CGFloat = 16
        static let horizontalGap: CGFloat = 16
        static let verticalGap: CGFloat = WaterfallMetrics.cardGap
        static let cardMinWidth: CGFloat = 220
        static let cardMaxWidth: CGFloat = 320
        static let titleBarHeight: CGFloat = 26
        static let titleGap: CGFloat = 6
        static let minThumbnailHeight: CGFloat = 92
        static let maxThumbnailHeightFraction: CGFloat = 0.40
        static let cardHeight: CGFloat = WaterfallMetrics.cardHeight
    }
    private enum WaterfallHorizontalIntent {
@@ -425,11 +673,15 @@
        case TriggerKeyCode.tab:
            performKeyboardCommand("tab")
        default:
            if handleAppShelfIndexKey(event) {
                return
            }
            super.keyDown(with: event)
        }
    }
    override func scrollWheel(with event: NSEvent) {
        clearWindowIndexKeyPending()
        refreshHoverTarget(at: event.locationInWindow)
        let dominantDelta = abs(event.scrollingDeltaX) >= abs(event.scrollingDeltaY)
@@ -485,13 +737,27 @@
    override func mouseMoved(with event: NSEvent) {
        guard debugHoveredAppGroupIndex == nil else { return }
        clearWindowIndexKeyPending()
        let settingsHovered = settingsButtonContains(windowLocation: event.locationInWindow)
        if isSettingsButtonHovered != settingsHovered {
            isSettingsButtonHovered = settingsHovered
        let toggleHovered = viewModeToggleButtonContains(windowLocation: event.locationInWindow)
        let themeHovered = themeToggleButtonContains(windowLocation: event.locationInWindow)
        if !toggleHovered {
            suppressViewModeToggleHoverPreview = false
        }
        if !themeHovered {
            suppressThemeToggleHoverPreview = false
        }
        let shouldShowToggleHoverPreview = toggleHovered && !suppressViewModeToggleHoverPreview
        if isViewModeToggleButtonHovered != shouldShowToggleHoverPreview {
            isViewModeToggleButtonHovered = shouldShowToggleHoverPreview
            needsLayout = true
        }
        if settingsHovered {
        let shouldShowThemeHoverPreview = themeHovered && !suppressThemeToggleHoverPreview
        if isThemeToggleButtonHovered != shouldShowThemeHoverPreview {
            isThemeToggleButtonHovered = shouldShowThemeHoverPreview
            needsLayout = true
        }
        if toggleHovered || themeHovered {
            hoveredCloseTarget = nil
            setHoverTarget(.none)
            return
@@ -507,7 +773,11 @@
    override func mouseExited(with event: NSEvent) {
        guard debugHoveredAppGroupIndex == nil else { return }
        isSettingsButtonHovered = false
        clearWindowIndexKeyPending()
        isViewModeToggleButtonHovered = false
        suppressViewModeToggleHoverPreview = false
        isThemeToggleButtonHovered = false
        suppressThemeToggleHoverPreview = false
        hoveredCloseTarget = nil
        setHoverTarget(.none)
    }
@@ -521,13 +791,19 @@
            ])
            return
        }
        clearWindowIndexKeyPending()
        if handleCloseConfirmationMouseDown(at: event.locationInWindow) {
            return
        }
        if settingsButtonContains(windowLocation: event.locationInWindow) {
            clickSettingsButton(source: "mouse")
        if viewModeToggleButtonContains(windowLocation: event.locationInWindow) {
            clickViewModeToggleButton(source: "mouse")
            return
        }
        if themeToggleButtonContains(windowLocation: event.locationInWindow) {
            clickThemeToggleButton(source: "mouse")
            return
        }
@@ -575,11 +851,49 @@
        layoutSubtreeIfNeeded()
        for command in commands {
            let measurementStart = CACurrentMediaTime()
            performKeyboardCommand(command)
            if !performDebugPhysicalIndexKeyCommand(command) {
                performKeyboardCommand(command)
            }
            layoutSubtreeIfNeeded()
            CATransaction.flush()
            keyboardResponseLatencyMilliseconds.append(milliseconds(since: measurementStart))
        }
    }
    private func performDebugPhysicalIndexKeyCommand(_ rawCommand: String) -> Bool {
        let command = rawCommand.trimmingCharacters(in: .whitespacesAndNewlines)
        let uppercased = command.uppercased()
        let prefixes = ["PHYSICAL-INDEX:", "PHYSICAL-INDEX-", "KEYDOWN:", "KEYDOWN-"]
        guard let prefix = prefixes.first(where: { uppercased.hasPrefix($0) }) else {
            return false
        }
        let symbolStart = command.index(command.startIndex, offsetBy: prefix.count)
        guard let symbol = appShelfIndexSymbol(fromKeyboardCommand: String(command[symbolStart...])),
              let keyCode = Self.appShelfIndexKeyCodes[symbol],
              let event = NSEvent.keyEvent(
                with: .keyDown,
                location: .zero,
                modifierFlags: [.option],
                timestamp: ProcessInfo.processInfo.systemUptime,
                windowNumber: window?.windowNumber ?? 0,
                context: nil,
                characters: symbol.lowercased(),
                charactersIgnoringModifiers: symbol.lowercased(),
                isARepeat: false,
                keyCode: keyCode
              )
        else {
            recordKeyboardCommand("invalidPhysicalIndex:\(command)")
            selectionChangedByLastCommand = false
            return true
        }
        let previousSuppressEventInput = suppressEventInput
        suppressEventInput = false
        keyDown(with: event)
        suppressEventInput = previousSuppressEventInput
        return true
    }
    func performDebugMouseCommands(_ commands: [String]) {
@@ -655,6 +969,15 @@
        needsLayout = true
    }
    func setTheme(_ theme: ThemePreference) {
        let normalizedTheme = Self.normalizedQuickSwitchTheme(theme)
        guard themePreference != normalizedTheme else { return }
        themePreference = normalizedTheme
        applyThemeToBackdrop()
        needsLayout = true
    }
    func showCloseProgress(for request: QuickSwitchCloseRequest) {
        setCloseFeedback(
            CloseFeedback(
@@ -700,7 +1023,8 @@
        if case .realScreenshot = resolution.source {
            let isSelected = card.item.window.id == effectiveSelection?.windowID
            let isHovered = card.item.window.id == hoveredWindowID
            card.shineLayer.opacity = shouldShowSelectedVisual(isSelected: isSelected, isHovered: isHovered) ? 1 : 0
            let isKeyboardFocused = card.item.window.id == keyboardFocusedWindowID
            card.shineLayer.opacity = (shouldShowSelectedVisual(isSelected: isSelected, isHovered: isHovered) || isKeyboardFocused) ? 1 : 0
        }
        needsLayout = true
    }
@@ -729,11 +1053,38 @@
        apply(viewModel: viewModel, preservingInteractionState: true, animatedProjection: true)
    }
    func hoverNextAppGroupIndex(cycleForward: Bool = true) -> Bool {
        guard !appShelfItems.isEmpty else {
            return false
        }
        let orderedIndexes = appShelfItems.map(\.item.appGroupIndex)
        let sortedIndexes = orderedIndexes
        var currentIndexPosition: Int?
        if let currentHovered = effectiveHoveredAppGroupIndex {
            currentIndexPosition = sortedIndexes.firstIndex(of: currentHovered)
        }
        let nextPosition = currentIndexPosition.map { current in
            if cycleForward {
                (current + 1) % sortedIndexes.count
            } else {
                (current - 1 + sortedIndexes.count) % sortedIndexes.count
            }
        } ?? 0
        let nextAppGroupIndex = sortedIndexes[nextPosition]
        hoverAppGroup(nextAppGroupIndex)
        return true
    }
    private func apply(
        viewModel: QuickSwitchViewModel?,
        preservingInteractionState: Bool,
        animatedProjection: Bool
    ) {
        let preservedWindowThumbnails = preservingInteractionState ? cachedThumbnailsByWindowID() : [:]
        let transitionSnapshot = animatedProjection ? captureProjectionTransitionSnapshot() : nil
        if !animatedProjection {
            resetProjectionTransitionReport()
@@ -744,6 +1095,16 @@
            currentSelection = validSelection(currentSelection, in: viewModel) ?? viewModel?.initialSelection
            columnSelectionHistory = columnSelectionHistory.filter { appGroupIndex, _ in
                viewModel?.waterfallColumns.contains { $0.appGroupIndex == appGroupIndex } == true
            }
            if let keyboardFocusedAppGroupIndex,
               viewModel?.waterfallColumns.contains(where: { $0.appGroupIndex == keyboardFocusedAppGroupIndex }) != true {
                self.keyboardFocusedAppGroupIndex = nil
            }
            if let keyboardFocusedWindowID,
               viewModel?.waterfallColumns.contains(where: { column in
                   column.windows.contains { $0.window.id == keyboardFocusedWindowID }
               }) != true {
                self.keyboardFocusedWindowID = nil
            }
            if let currentSelection {
                columnSelectionHistory[currentSelection.appGroupIndex] = currentSelection.windowIndex
@@ -759,8 +1120,22 @@
            screenshotSkippedReasonsByWindowID = [:]
            currentSelection = viewModel?.initialSelection
            columnSelectionHistory = [:]
            keyboardFocusedAppGroupIndex = nil
            keyboardFocusedWindowID = nil
            keyboardCommandsApplied = []
            lastKeyboardCommand = nil
            lastKeyboardAppIndexCommand = nil
            appShelfIndexKeyDownCount = 0
            lastAppShelfIndexKeySymbol = nil
            clearWindowIndexKeyPending()
            lastWindowIndexKeyCommand = nil
            lastWindowIndexKeyCommitCode = nil
            windowShortcutFilterPhase = .none
            lastWindowShortcutPreCommitFilterSnapshot = nil
            lastBoundaryBounceAxis = nil
            lastBoundaryBounceDirection = nil
            boundaryBounceCount = 0
            selectionChangedByLastCommand = nil
            tabIgnoredCount = 0
            lastCommittedSelection = nil
            lastCommitSource = nil
@@ -794,12 +1169,35 @@
        rebuildSpaceLaneSegments()
        rebuildAppShelfItems()
        rebuildWaterfallColumns()
        if preservingInteractionState {
            restoreThumbnailsFromCache(preservedWindowThumbnails)
        }
        needsLayout = true
        ensureCurrentSelectionVisible(horizontalIntent: .alignWithAppShelf(animated: false))
        needsLayout = true
        if animatedProjection, let transitionSnapshot {
            layoutSubtreeIfNeeded()
            applyProjectionTransitionAnimations(from: transitionSnapshot)
        }
    }
    private func cachedThumbnailsByWindowID() -> [UInt32: Any] {
        var snapshots: [UInt32: Any] = [:]
        for card in waterfallCardsByWindowID().values {
            snapshots[card.item.window.id] = card.thumbnailLayer.contents
        }
        return snapshots
    }
    private func restoreThumbnailsFromCache(_ cachedThumbnails: [UInt32: Any]) {
        for card in waterfallCardsByWindowID().values {
            if let thumbnail = cachedThumbnails[card.item.window.id] {
                card.thumbnailLayer.contents = thumbnail
            }
        }
    }
@@ -1052,6 +1450,7 @@
        let firstFrameGatePassed = firstFrameP95.map { $0 < 150 } ?? false
        let keyboardGatePassed = keyboardP95.map { $0 < 50 } ?? false
        let hoverGatePassed = hoverP95.map { $0 <= 32 } ?? false
        let shortcutFilterReport = makeWindowShortcutFilterReport()
        return [
            "viewClass": String(describing: Self.self),
@@ -1082,9 +1481,34 @@
            "activeSpaceFocusID": activeSpaceFocusID ?? NSNull(),
            "spaceFilterLockedSpaceID": currentViewModel?.lockedSpaceID ?? NSNull(),
            "spaceFilterActive": currentViewModel?.lockedSpaceID != nil,
            "settingsButtonVisible": !settingsButtonLayer.isHidden,
            "settingsButtonHovered": isSettingsButtonHovered,
            "settingsButtonFrame": dictionary(from: settingsButtonLayer.frame),
            "viewModeToggleButtonVisible": !viewModeToggleButtonLayer.isHidden,
            "viewModeToggleButtonHovered": isViewModeToggleButtonHovered,
            "viewModeToggleHoverPreviewSuppressed": suppressViewModeToggleHoverPreview,
            "viewModeToggleButtonFrame": dictionary(from: viewModeToggleButtonLayer.frame),
            "viewModeToggleHitSize": Double(ViewModeToggleMetrics.hitSize),
            "viewModeToggleVisualSize": Double(ViewModeToggleMetrics.visualSize),
            "viewModeToggleCurrentMode": waterfallViewMode.rawValue,
            "viewModeToggleTargetMode": nextWaterfallViewMode.rawValue,
            "viewModeToggleIconKind": viewModeToggleIconKind,
            "viewModeToggleVisualState": viewModeToggleVisualState.rawValue,
            "viewModeToggleStandardBlueColor": colorDictionary(from: viewModeToggleBlueColor.cgColor),
            "viewModeToggleGlyphLineWidth": Double(ViewModeToggleMetrics.glyphLineWidth),
            "viewModeToggleAccessibilityLabel": viewModeToggleAccessibilityLabel,
            "themeCurrentPreference": themePreference.rawValue,
            "themeResolved": resolvedTheme.rawValue,
            "themeNextPreference": nextThemePreference.rawValue,
            "themeToggleButtonVisible": !themeToggleButtonLayer.isHidden,
            "themeToggleButtonHovered": isThemeToggleButtonHovered,
            "themeToggleHoverPreviewSuppressed": suppressThemeToggleHoverPreview,
            "themeToggleButtonFrame": dictionary(from: themeToggleButtonLayer.frame),
            "themeToggleHitSize": Double(ViewModeToggleMetrics.hitSize),
            "themeToggleVisualSize": Double(ViewModeToggleMetrics.visualSize),
            "themeToggleIconKind": "halfDisk",
            "themeToggleAccessibilityLabel": themeToggleAccessibilityLabel,
            "darkBackdropLayerCount": darkBackdropLayerCount,
            "darkBackdropLayersVisible": darkBackdropLayersVisible,
            "darkBackdropBackgroundGradientColors": backgroundGradientColorReports(),
            "settingsButtonVisible": false,
            "spaceFocusAppGroupIndexes": spaceFocusedAppGroupIndexes(),
            "appHoverAssociatedSpaceIDs": appHoverAssociatedSpaceIDs.sorted(),
            "appHoverAssociatedSpaceLabels": appHoverAssociatedSpaceLabels,
@@ -1145,8 +1569,31 @@
            "selectedAppGroupIndex": effectiveSelection?.appGroupIndex ?? NSNull(),
            "selectedWindowIndex": effectiveSelection?.windowIndex ?? NSNull(),
            "selectedWindowID": effectiveSelection?.windowID ?? NSNull(),
            "keyboardFocusedAppGroupIndex": keyboardFocusedAppGroupIndex ?? NSNull(),
            "keyboardFocusedWindowID": keyboardFocusedWindowID ?? NSNull(),
            "horizontalAppDefaultWindowID": horizontalAppDefaultWindowID ?? NSNull(),
            "keyboardCommandsApplied": keyboardCommandsApplied,
            "lastKeyboardCommand": lastKeyboardCommand ?? NSNull(),
            "lastKeyboardAppIndexCommand": lastKeyboardAppIndexCommand ?? NSNull(),
            "appShelfIndexKeyDownCount": appShelfIndexKeyDownCount,
            "lastAppShelfIndexKeySymbol": lastAppShelfIndexKeySymbol ?? NSNull(),
            "windowIndexKeyPendingAppGroupIndex": windowIndexKeyPending?.appGroupIndex ?? NSNull(),
            "windowIndexKeyPendingAppSymbol": windowIndexKeyPending?.appSymbol ?? NSNull(),
            "lastWindowIndexKeyCommand": lastWindowIndexKeyCommand ?? NSNull(),
            "lastWindowIndexKeyCommitCode": lastWindowIndexKeyCommitCode ?? NSNull(),
            "windowShortcutFilterActive": shortcutFilterReport.active,
            "windowShortcutFilterPhase": shortcutFilterReport.phase.rawValue,
            "windowShortcutFilterPendingAppGroupIndex": shortcutFilterReport.pendingAppGroupIndex ?? NSNull(),
            "windowShortcutFilterPrefix": shortcutFilterReport.prefix ?? NSNull(),
            "windowShortcutFilterMatchedCount": shortcutFilterReport.matchedWindowIDs.count,
            "windowShortcutFilterDimmedCount": shortcutFilterReport.dimmedWindowIDs.count,
            "windowShortcutFilterMatchedWindowIDs": shortcutFilterReport.matchedWindowIDs.map(Int.init),
            "windowShortcutFilterDimmedWindowIDs": shortcutFilterReport.dimmedWindowIDs.map(Int.init),
            "lastWindowShortcutPreCommitFilterSnapshot": lastWindowShortcutPreCommitFilterSnapshot?.reportDictionary ?? NSNull(),
            "lastBoundaryBounceAxis": lastBoundaryBounceAxis ?? NSNull(),
            "lastBoundaryBounceDirection": lastBoundaryBounceDirection ?? NSNull(),
            "boundaryBounceCount": boundaryBounceCount,
            "selectionChangedByLastCommand": selectionChangedByLastCommand ?? NSNull(),
            "tabIgnoredCount": tabIgnoredCount,
            "lastCommittedAppGroupIndex": lastCommittedSelection?.appGroupIndex ?? NSNull(),
            "lastCommittedWindowIndex": lastCommittedSelection?.windowIndex ?? NSNull(),
@@ -1219,32 +1666,68 @@
            .lowercased()
        guard !command.isEmpty else { return }
        if let indexSymbol = appShelfIndexSymbol(fromKeyboardCommand: command) {
            let recordedCommand = "app:\(indexSymbol)"
            recordKeyboardCommand(recordedCommand)
            clearWindowIndexKeyPending()
            if !focusAppShelfItemForKeyboard(indexSymbol: indexSymbol, commandLabel: recordedCommand) {
                selectionChangedByLastCommand = false
            }
            return
        }
        switch command {
        case "left":
            clearWindowIndexKeyPending()
            recordKeyboardCommand("left")
            moveSelection(.left)
            if isHorizontalMasonryMode {
                moveSelection(.left)
            } else if !moveKeyboardAppFocus(cycleForward: false) {
                selectionChangedByLastCommand = false
            }
        case "right":
            clearWindowIndexKeyPending()
            recordKeyboardCommand("right")
            moveSelection(.right)
            if isHorizontalMasonryMode {
                moveSelection(.right)
            } else if !moveKeyboardAppFocus(cycleForward: true) {
                selectionChangedByLastCommand = false
            }
        case "up":
            clearWindowIndexKeyPending()
            recordKeyboardCommand("up")
            moveSelection(.up)
            if isHorizontalMasonryMode {
                moveSelection(.up)
            } else {
                moveVerticalSelectionWithinFocusedColumn(.up)
            }
        case "down":
            clearWindowIndexKeyPending()
            recordKeyboardCommand("down")
            moveSelection(.down)
            if isHorizontalMasonryMode {
                moveSelection(.down)
            } else {
                moveVerticalSelectionWithinFocusedColumn(.down)
            }
        case "enter", "return", "commit":
            clearWindowIndexKeyPending()
            recordKeyboardCommand("enter")
            selectionChangedByLastCommand = false
            commitCurrentSelection()
        case "tab":
            clearWindowIndexKeyPending()
            if isHorizontalMasonryMode {
                recordKeyboardCommand("tab")
                moveSelection(.right)
                moveHorizontalMasonryTabSelection()
            } else {
                tabIgnoredCount += 1
                recordKeyboardCommand("tabIgnored")
                selectionChangedByLastCommand = false
            }
        default:
            clearWindowIndexKeyPending()
            recordKeyboardCommand("unknown:\(command)")
            selectionChangedByLastCommand = false
        }
    }
@@ -1253,11 +1736,407 @@
        keyboardCommandsApplied.append(command)
    }
    private func handleAppShelfIndexKey(_ event: NSEvent) -> Bool {
        let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
        guard flags.isDisjoint(with: [.command, .control]) else { return false }
        guard let characters = event.charactersIgnoringModifiers,
              let symbol = appShelfIndexSymbol(fromKeyboardCommand: characters)
        else {
            return false
        }
        appShelfIndexKeyDownCount += 1
        lastAppShelfIndexKeySymbol = symbol
        if flags.contains(.option), handleWindowIndexKey(symbol: symbol) {
            return true
        }
        clearWindowIndexKeyPending()
        performKeyboardCommand(symbol)
        return true
    }
    private func handleWindowIndexKey(symbol: String) -> Bool {
        let now = CACurrentMediaTime()
        if let pending = validWindowIndexKeyPending(now: now) {
            let code = "\(pending.appSymbol)\(symbol)"
            if let card = waterfallCard(appGroupIndex: pending.appGroupIndex, windowIndexSymbol: symbol) {
                recordKeyboardCommand("window:\(code)")
                lastWindowIndexKeyCommand = "window:\(code)"
                lastWindowIndexKeyCommitCode = code
                lastWindowShortcutPreCommitFilterSnapshot = makeWindowShortcutPreCommitFilterSnapshot(
                    target: card,
                    code: code
                )
                windowShortcutFilterPhase = .targetPreCommit
                clearWindowIndexKeyPending(resetShortcutFilter: false)
                commitKeyboardWindowCard(card, trigger: "windowIndexKey")
                return true
            }
            recordKeyboardCommand("windowInvalid:\(code)")
            lastWindowIndexKeyCommand = "windowInvalid:\(code)"
            lastWindowIndexKeyCommitCode = nil
            windowShortcutFilterPhase = .invalid
            lastWindowShortcutPreCommitFilterSnapshot = nil
            selectionChangedByLastCommand = false
            clearWindowIndexKeyPending(resetShortcutFilter: false)
            return true
        }
        let recordedCommand = "app:\(symbol)"
        recordKeyboardCommand(recordedCommand)
        lastWindowIndexKeyCommand = nil
        lastWindowIndexKeyCommitCode = nil
        if focusAppShelfItemForKeyboard(indexSymbol: symbol, commandLabel: recordedCommand),
           let ordinal = Self.appShelfIndexSymbols.firstIndex(of: symbol),
           ordinal < appShelfItems.count {
            let appGroupIndex = appShelfItems[ordinal].item.appGroupIndex
            if let card = singleVisibleWaterfallCard(appGroupIndex: appGroupIndex) {
                recordKeyboardCommand("windowSingle:\(symbol)")
                lastWindowIndexKeyCommand = "windowSingle:\(symbol)"
                lastWindowIndexKeyCommitCode = symbol
                lastWindowShortcutPreCommitFilterSnapshot = nil
                clearWindowIndexKeyPending()
                commitKeyboardWindowCard(card, trigger: "windowIndexSingleApp")
            } else {
                setWindowIndexKeyPending(
                    appSymbol: symbol,
                    appGroupIndex: appGroupIndex,
                    startedAt: now
                )
            }
        } else {
            selectionChangedByLastCommand = false
            clearWindowIndexKeyPending()
        }
        return true
    }
    private func appShelfIndexSymbol(fromKeyboardCommand command: String) -> String? {
        var candidate = command.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
        for prefix in ["APP:", "APP-", "INDEX:", "INDEX-"] where candidate.hasPrefix(prefix) {
            candidate.removeFirst(prefix.count)
            break
        }
        guard candidate.count == 1,
              Self.appShelfIndexSymbols.contains(candidate)
        else {
            return nil
        }
        return candidate
    }
    private func focusAppShelfItemForKeyboard(indexSymbol: String, commandLabel: String) -> Bool {
        guard let ordinal = Self.appShelfIndexSymbols.firstIndex(of: indexSymbol),
              ordinal < appShelfItems.count
        else {
            return false
        }
        return focusAppShelfItemForKeyboard(
            appGroupIndex: appShelfItems[ordinal].item.appGroupIndex,
            appIndexCommand: commandLabel
        )
    }
    private func validWindowIndexKeyPending(now: CFTimeInterval) -> WindowIndexKeyPending? {
        guard let pending = windowIndexKeyPending else { return nil }
        guard now - pending.startedAt <= Self.windowIndexKeyPendingInterval else {
            clearWindowIndexKeyPending()
            return nil
        }
        return pending
    }
    private func setWindowIndexKeyPending(appSymbol: String, appGroupIndex: Int, startedAt: CFTimeInterval) {
        windowIndexKeyPendingClearTask?.cancel()
        windowIndexKeyPending = WindowIndexKeyPending(
            appSymbol: appSymbol,
            appGroupIndex: appGroupIndex,
            startedAt: startedAt
        )
        windowShortcutFilterPhase = .appPending
        lastWindowShortcutPreCommitFilterSnapshot = nil
        needsLayout = true
        windowIndexKeyPendingClearTask = Task { @MainActor [weak self] in
            do {
                try await Task.sleep(nanoseconds: UInt64(Self.windowIndexKeyPendingInterval * 1_000_000_000))
            } catch {
                return
            }
            guard let self,
                  let pending = self.windowIndexKeyPending,
                  pending.appSymbol == appSymbol,
                  pending.appGroupIndex == appGroupIndex,
                  pending.startedAt == startedAt
            else {
                return
            }
            self.windowIndexKeyPending = nil
            self.windowIndexKeyPendingClearTask = nil
            self.windowShortcutFilterPhase = .expired
            self.lastWindowShortcutPreCommitFilterSnapshot = nil
            self.needsLayout = true
        }
    }
    private func clearWindowIndexKeyPending(resetShortcutFilter: Bool = true) {
        let hadFilterState = windowIndexKeyPending != nil
            || windowShortcutFilterPhase != .none
            || lastWindowShortcutPreCommitFilterSnapshot != nil
        windowIndexKeyPendingClearTask?.cancel()
        windowIndexKeyPendingClearTask = nil
        windowIndexKeyPending = nil
        if resetShortcutFilter {
            windowShortcutFilterPhase = .none
            lastWindowShortcutPreCommitFilterSnapshot = nil
        }
        if hadFilterState {
            needsLayout = true
        }
    }
    @discardableResult
    private func focusAppShelfItemForKeyboard(
        appGroupIndex: Int,
        appIndexCommand: String? = nil
    ) -> Bool {
        guard appShelfItems.contains(where: { $0.item.appGroupIndex == appGroupIndex }),
              waterfallColumns.contains(where: { $0.column.appGroupIndex == appGroupIndex })
        else {
            return false
        }
        keyboardFocusedAppGroupIndex = appGroupIndex
        keyboardFocusedWindowID = nil
        lastKeyboardAppIndexCommand = appIndexCommand
        selectionChangedByLastCommand = false
        clearKeyboardBoundaryFeedback()
        ensureAppShelfItemVisible(appGroupIndex)
        layoutSubtreeIfNeeded()
        if isHorizontalMasonryMode {
            _ = selectHorizontalMasonryDefaultWindowForApp(appGroupIndex)
        }
        hoverAppGroup(appGroupIndex, source: .keyboard)
        needsLayout = true
        return true
    }
    @discardableResult
    private func moveKeyboardAppFocus(cycleForward: Bool) -> Bool {
        let orderedAppGroupIndexes = appShelfItems.map(\.item.appGroupIndex)
        guard !orderedAppGroupIndexes.isEmpty else { return false }
        let currentAppGroupIndex = keyboardFocusedAppGroupIndex
            ?? appHoverTargetAppGroupIndex
            ?? effectiveSelection?.appGroupIndex
        let currentPosition = currentAppGroupIndex.flatMap { orderedAppGroupIndexes.firstIndex(of: $0) }
        let nextPosition = currentPosition.map { current in
            if cycleForward {
                return (current + 1) % orderedAppGroupIndexes.count
            }
            return (current - 1 + orderedAppGroupIndexes.count) % orderedAppGroupIndexes.count
        } ?? (cycleForward ? 0 : orderedAppGroupIndexes.count - 1)
        return focusAppShelfItemForKeyboard(appGroupIndex: orderedAppGroupIndexes[nextPosition])
    }
    private var appHoverTargetAppGroupIndex: Int? {
        guard case .app(let appGroupIndex) = hoverTarget else { return nil }
        return appGroupIndex
    }
    private var horizontalAppDefaultWindowID: UInt32? {
        guard isHorizontalMasonryMode,
              case .app(let appGroupIndex) = hoverTarget
        else {
            return nil
        }
        return firstHorizontalMasonryCard(for: appGroupIndex)?.item.window.id
    }
    private func firstHorizontalMasonryCard(for appGroupIndex: Int) -> WaterfallCardLayers? {
        waterfallColumns
            .first { $0.column.appGroupIndex == appGroupIndex }?
            .cards
            .sorted { $0.item.windowIndex < $1.item.windowIndex }
            .first
    }
    private func isHorizontalAppDefaultCard(_ card: WaterfallCardLayers) -> Bool {
        guard isHorizontalMasonryMode,
              card.item.window.id == horizontalAppDefaultWindowID
        else {
            return false
        }
        return true
    }
    @discardableResult
    private func selectHorizontalMasonryDefaultWindowForApp(
        _ appGroupIndex: Int,
        recordsSelectionChange: Bool = false
    ) -> QuickSwitchSelection? {
        guard isHorizontalMasonryMode,
              let card = firstHorizontalMasonryCard(for: appGroupIndex)
        else {
            return nil
        }
        let previousSelection = effectiveSelection
        let selection = selectionForCard(card)
        currentSelection = selection
        columnSelectionHistory[selection.appGroupIndex] = selection.windowIndex
        if recordsSelectionChange {
            selectionChangedByLastCommand = previousSelection != selection
        }
        return selection
    }
    private func moveVerticalSelectionWithinFocusedColumn(_ direction: QuickSwitchKeyboardDirection) {
        guard direction == .up || direction == .down,
              let targetAppGroupIndex = verticalKeyboardNavigationAppGroupIndex(),
              let column = waterfallColumns.first(where: { $0.column.appGroupIndex == targetAppGroupIndex })
        else {
            DevelopmentDiagnostics.log("quickSwitch.view.moveVerticalSelection.blocked", [
                "direction": keyboardDirectionName(direction),
                "hasFocusedApp": keyboardFocusedAppGroupIndex != nil,
                "hasSelection": effectiveSelection != nil
            ])
            selectionChangedByLastCommand = false
            return
        }
        let cards = column.cards.sorted { $0.item.windowIndex < $1.item.windowIndex }
        guard !cards.isEmpty else {
            selectionChangedByLastCommand = false
            return
        }
        let previousSelection = effectiveSelection
        let nextSelection: QuickSwitchSelection
        if let current = previousSelection,
           current.appGroupIndex == targetAppGroupIndex,
           let currentPosition = cards.firstIndex(where: { $0.item.window.id == current.windowID }) {
            let nextPosition = currentPosition + (direction == .down ? 1 : -1)
            guard cards.indices.contains(nextPosition) else {
                keyboardFocusedAppGroupIndex = targetAppGroupIndex
                hoverAppGroup(targetAppGroupIndex, source: .keyboard)
                triggerKeyboardBoundaryFeedback(appGroupIndex: targetAppGroupIndex, direction: direction)
                DevelopmentDiagnostics.log("quickSwitch.view.moveVerticalSelection.boundary", [
                    "direction": keyboardDirectionName(direction),
                    "appGroupIndex": targetAppGroupIndex,
                    "windowIndex": current.windowIndex
                ])
                needsLayout = true
                return
            }
            nextSelection = selectionForCard(cards[nextPosition])
        } else {
            let rememberedWindowIndex = columnSelectionHistory[targetAppGroupIndex]
            let rememberedCard = rememberedWindowIndex.flatMap { windowIndex in
                cards.first { $0.item.windowIndex == windowIndex }
            }
            nextSelection = selectionForCard(rememberedCard ?? cards[0])
        }
        keyboardFocusedAppGroupIndex = targetAppGroupIndex
        currentSelection = nextSelection
        keyboardFocusedWindowID = nextSelection.windowID
        columnSelectionHistory[nextSelection.appGroupIndex] = nextSelection.windowIndex
        selectionChangedByLastCommand = previousSelection != nextSelection
        clearKeyboardBoundaryFeedback()
        hoverAppGroup(targetAppGroupIndex, source: .keyboard)
        ensureCurrentSelectionVisible(horizontalIntent: .alignWithAppShelf(animated: true))
        DevelopmentDiagnostics.log("quickSwitch.view.moveVerticalSelection", [
            "direction": keyboardDirectionName(direction),
            "appGroupIndex": nextSelection.appGroupIndex,
            "windowIndex": nextSelection.windowIndex,
            "windowID": nextSelection.windowID
        ])
        needsLayout = true
    }
    private func verticalKeyboardNavigationAppGroupIndex() -> Int? {
        if let keyboardFocusedAppGroupIndex,
           waterfallColumns.contains(where: { $0.column.appGroupIndex == keyboardFocusedAppGroupIndex }) {
            return keyboardFocusedAppGroupIndex
        }
        if let appHoverTargetAppGroupIndex,
           waterfallColumns.contains(where: { $0.column.appGroupIndex == appHoverTargetAppGroupIndex }) {
            return appHoverTargetAppGroupIndex
        }
        if let appGroupIndex = effectiveSelection?.appGroupIndex,
           waterfallColumns.contains(where: { $0.column.appGroupIndex == appGroupIndex }) {
            return appGroupIndex
        }
        return waterfallColumns.first?.column.appGroupIndex
    }
    private func clearKeyboardBoundaryFeedback() {
        lastBoundaryBounceAxis = nil
        lastBoundaryBounceDirection = nil
    }
    private func triggerKeyboardBoundaryFeedback(
        appGroupIndex: Int,
        direction: QuickSwitchKeyboardDirection
    ) {
        selectionChangedByLastCommand = false
        lastBoundaryBounceAxis = "vertical"
        lastBoundaryBounceDirection = keyboardDirectionName(direction)
        boundaryBounceCount += 1
        layoutSubtreeIfNeeded()
        guard let column = waterfallColumns.first(where: { $0.column.appGroupIndex == appGroupIndex }) else { return }
        let animationKey = "keyboardBoundaryBounce"
        column.containerLayer.removeAnimation(forKey: animationKey)
        if NSWorkspace.shared.accessibilityDisplayShouldReduceMotion {
            let pulse = CABasicAnimation(keyPath: "opacity")
            pulse.fromValue = 1
            pulse.toValue = 0.9
            pulse.duration = 0.12
            pulse.autoreverses = true
            pulse.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
            column.containerLayer.add(pulse, forKey: animationKey)
            return
        }
        let currentY = column.containerLayer.presentation()?.position.y
            ?? column.containerLayer.position.y
        let delta: CGFloat = direction == .up ? 5 : -5
        let bounce = CAKeyframeAnimation(keyPath: "position.y")
        bounce.values = [currentY, currentY + delta, currentY]
        bounce.keyTimes = [0, 0.38, 1]
        bounce.timingFunctions = [
            CAMediaTimingFunction(name: .easeOut),
            CAMediaTimingFunction(name: .easeInEaseOut)
        ]
        bounce.duration = 0.23
        bounce.isRemovedOnCompletion = true
        column.containerLayer.add(bounce, forKey: animationKey)
    }
    private func keyboardDirectionName(_ direction: QuickSwitchKeyboardDirection) -> String {
        switch direction {
        case .left:
            return "left"
        case .right:
            return "right"
        case .up:
            return "up"
        case .down:
            return "down"
        }
    }
    private func performMouseCommand(_ rawCommand: String) {
        let command = rawCommand
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .lowercased()
        guard !command.isEmpty else { return }
        clearWindowIndexKeyPending()
        let parts = command.split(separator: ":").map(String.init)
        switch parts.first {
@@ -1327,9 +2206,26 @@
        case "click-background":
            recordMouseCommand("click-background")
            _ = onBackgroundClick?()
        case "click-settings":
            recordMouseCommand("click-settings")
            clickSettingsButton(source: "debug")
        case "hover-view-toggle":
            recordMouseCommand("hover-view-toggle")
            suppressViewModeToggleHoverPreview = false
            isViewModeToggleButtonHovered = true
            isThemeToggleButtonHovered = false
            hoveredCloseTarget = nil
            setHoverTarget(.none, source: .debug)
        case "click-view-toggle", "click-settings":
            recordMouseCommand("click-view-toggle")
            clickViewModeToggleButton(source: "debug")
        case "hover-theme-toggle":
            recordMouseCommand("hover-theme-toggle")
            suppressThemeToggleHoverPreview = false
            isThemeToggleButtonHovered = true
            isViewModeToggleButtonHovered = false
            hoveredCloseTarget = nil
            setHoverTarget(.none, source: .debug)
        case "click-theme-toggle":
            recordMouseCommand("click-theme-toggle")
            clickThemeToggleButton(source: "debug")
        case "hover-card" where parts.count == 3:
            guard let appGroupIndex = Int(parts[1]), let windowIndex = Int(parts[2]) else {
                recordMouseCommand("invalid:\(command)")
@@ -1346,6 +2242,15 @@
            }
            recordMouseCommand("click-card:\(appGroupIndex):\(windowIndex)")
            if let card = waterfallCard(appGroupIndex: appGroupIndex, windowIndex: windowIndex) {
                clickWindowCard(card)
            }
        case "click-card-id" where parts.count == 2:
            guard let windowID = UInt32(parts[1]) else {
                recordMouseCommand("invalid:\(command)")
                return
            }
            recordMouseCommand("click-card-id:\(windowID)")
            if let card = waterfallCard(windowID: windowID) {
                clickWindowCard(card)
            }
        case "hover-card-close" where parts.count == 3:
@@ -1388,13 +2293,13 @@
        return command.hasPrefix("hover-")
    }
    private func hoverAppGroup(_ appGroupIndex: Int) {
    private func hoverAppGroup(_ appGroupIndex: Int, source: HoverTargetSource = .debug) {
        updateHoverState(
            appGroupIndex: appGroupIndex,
            windowID: nil,
            spaceID: nil,
            alignsWaterfallWithAppShelf: true,
            source: .debug
            source: source
        )
    }
@@ -1553,6 +2458,34 @@
        commitWindowCard(card, trigger: "windowCard")
    }
    private func commitKeyboardWindowCard(_ card: WaterfallCardLayers, trigger: String) {
        let selection = QuickSwitchSelection(
            appGroupIndex: card.item.appGroupIndex,
            windowIndex: card.item.windowIndex,
            windowID: card.item.window.id
        )
        DevelopmentDiagnostics.log("quickSwitch.view.commitKeyboardWindow", [
            "trigger": trigger,
            "appGroupIndex": selection.appGroupIndex,
            "windowIndex": selection.windowIndex,
            "windowID": selection.windowID,
            "appName": card.item.window.app.name,
            "appPID": card.item.window.app.processIdentifier,
            "titleHash": DevelopmentDiagnostics.stableFingerprint(card.item.window.title),
            "titleLength": card.item.window.title.count,
            "titleIsEmpty": card.item.window.title.isEmpty,
            "isMinimized": card.item.window.isMinimized,
            "isFullscreen": card.item.window.isFullscreen,
            "identifierSource": String(describing: card.item.window.identifierSource)
        ])
        currentSelection = selection
        columnSelectionHistory[selection.appGroupIndex] = selection.windowIndex
        lastCommittedSelection = selection
        lastCommitSource = .keyboard
        ensureCurrentSelectionVisible()
        onCommitSelection?(selection, .keyboard)
    }
    private func commitWindowCard(_ card: WaterfallCardLayers, trigger: String) {
        let selection = QuickSwitchSelection(
            appGroupIndex: card.item.appGroupIndex,
@@ -1642,6 +2575,11 @@
        hoveredSpaceID = target.spaceID
        hoveredSpaceLaneID = target.spaceLaneID
        if case .app(let appGroupIndex) = target, isHorizontalMasonryMode {
            keyboardFocusedWindowID = nil
            _ = selectHorizontalMasonryDefaultWindowForApp(appGroupIndex)
        }
        if alignsWaterfallWithAppShelf, let appGroupIndex = target.appGroupIndex {
            alignWaterfallColumnWithAppShelfIcon(appGroupIndex: appGroupIndex, animated: true)
        }
@@ -1675,8 +2613,11 @@
        }
        columnSelectionHistory[current.appGroupIndex] = current.windowIndex
        let previousSelection = effectiveSelection
        currentSelection = next
        columnSelectionHistory[next.appGroupIndex] = next.windowIndex
        selectionChangedByLastCommand = previousSelection != next
        clearKeyboardBoundaryFeedback()
        ensureCurrentSelectionVisible(horizontalIntent: .alignWithAppShelf(animated: true))
        DevelopmentDiagnostics.log("quickSwitch.view.moveSelection", [
            "direction": String(describing: direction),
@@ -1688,26 +2629,360 @@
    }
    private func moveHorizontalMasonrySelection(_ direction: QuickSwitchKeyboardDirection) {
        guard let current = currentSelection ?? currentViewModel?.initialSelection,
              let next = nextHorizontalMasonrySelection(from: current, direction: direction)
        layoutSubtreeIfNeeded()
        let nodes = horizontalKeyboardFocusNodes()
        guard !nodes.isEmpty,
              let current = currentHorizontalKeyboardFocusNode(in: nodes),
              let next = nextHorizontalKeyboardFocusNode(from: current, direction: direction, in: nodes)
        else {
            DevelopmentDiagnostics.log("quickSwitch.view.moveHorizontalMasonrySelection.blocked", [
                "direction": String(describing: direction),
                "hasNodes": !nodes.isEmpty,
                "hasSelection": (currentSelection ?? currentViewModel?.initialSelection) != nil
            ])
            selectionChangedByLastCommand = false
            return
        }
        focusHorizontalKeyboardNode(next, direction: direction)
        DevelopmentDiagnostics.log("quickSwitch.view.moveHorizontalMasonrySelection", [
            "direction": String(describing: direction),
            "kind": next.kind.rawValue,
            "appGroupIndex": next.appGroupIndex,
            "windowIndex": next.windowIndex ?? -1,
            "windowID": next.windowID ?? 0
        ])
    }
    private func moveHorizontalMasonryTabSelection() {
        guard let current = currentSelection ?? currentViewModel?.initialSelection,
              let next = nextHorizontalMasonrySelection(from: current, direction: .right)
        else {
            DevelopmentDiagnostics.log("quickSwitch.view.moveHorizontalMasonryTabSelection.blocked", [
                "hasSelection": (currentSelection ?? currentViewModel?.initialSelection) != nil
            ])
            selectionChangedByLastCommand = false
            return
        }
        columnSelectionHistory[current.appGroupIndex] = current.windowIndex
        let previousSelection = effectiveSelection
        currentSelection = next
        keyboardFocusedAppGroupIndex = next.appGroupIndex
        keyboardFocusedWindowID = next.windowID
        columnSelectionHistory[next.appGroupIndex] = next.windowIndex
        selectionChangedByLastCommand = previousSelection != next
        clearKeyboardBoundaryFeedback()
        setHoverTarget(
            .window(appGroupIndex: next.appGroupIndex, windowID: next.windowID, spaceID: nil),
            source: .keyboard
        )
        ensureCurrentSelectionVisible(horizontalIntent: .alignWithAppShelf(animated: true))
        DevelopmentDiagnostics.log("quickSwitch.view.moveHorizontalMasonrySelection", [
            "direction": String(describing: direction),
        DevelopmentDiagnostics.log("quickSwitch.view.moveHorizontalMasonryTabSelection", [
            "appGroupIndex": next.appGroupIndex,
            "windowIndex": next.windowIndex,
            "windowID": next.windowID
        ])
        needsLayout = true
    }
    private func horizontalKeyboardFocusNodes() -> [HorizontalKeyboardFocusNode] {
        var nodes: [HorizontalKeyboardFocusNode] = []
        nodes.reserveCapacity(appShelfItems.count + waterfallColumns.reduce(0) { $0 + $1.cards.count })
        for (index, item) in appShelfItems.enumerated() {
            let frame = item.iconLayer.frame.offsetBy(
                dx: shelfLayer.frame.minX
                    + appShelfContentLayer.frame.minX
                    + item.containerLayer.frame.minX,
                dy: shelfLayer.frame.minY
                    + appShelfContentLayer.frame.minY
                    + item.containerLayer.frame.minY
            )
            nodes.append(HorizontalKeyboardFocusNode(
                kind: .app,
                appGroupIndex: item.item.appGroupIndex,
                windowIndex: nil,
                windowID: nil,
                spaceID: nil,
                frame: frame,
                order: index
            ))
        }
        var order = appShelfItems.count
        for column in waterfallColumns.sorted(by: { $0.column.appGroupIndex < $1.column.appGroupIndex }) {
            for card in column.cards.sorted(by: { $0.item.windowIndex < $1.item.windowIndex }) {
                let frame = card.containerLayer.frame.offsetBy(
                    dx: waterfallLayer.frame.minX
                        + waterfallContentLayer.frame.minX
                        + column.containerLayer.frame.minX
                        + column.cardsClipLayer.frame.minX,
                    dy: waterfallLayer.frame.minY
                        + waterfallContentLayer.frame.minY
                        + column.containerLayer.frame.minY
                        + column.cardsClipLayer.frame.minY
                )
                nodes.append(HorizontalKeyboardFocusNode(
                    kind: .window,
                    appGroupIndex: card.item.appGroupIndex,
                    windowIndex: card.item.windowIndex,
                    windowID: card.item.window.id,
                    spaceID: card.item.primarySpaceID,
                    frame: frame,
                    order: order
                ))
                order += 1
            }
        }
        return nodes
    }
    private func currentHorizontalKeyboardFocusNode(
        in nodes: [HorizontalKeyboardFocusNode]
    ) -> HorizontalKeyboardFocusNode? {
        if case .app(let appGroupIndex) = hoverTarget,
           let node = nodes.first(where: { $0.kind == .app && $0.appGroupIndex == appGroupIndex }) {
            return node
        }
        if let keyboardFocusedWindowID,
           let node = nodes.first(where: { $0.kind == .window && $0.windowID == keyboardFocusedWindowID }) {
            return node
        }
        if case .window(_, let windowID, _) = hoverTarget,
           let node = nodes.first(where: { $0.kind == .window && $0.windowID == windowID }) {
            return node
        }
        if let selection = effectiveSelection,
           let node = nodes.first(where: { $0.kind == .window && $0.windowID == selection.windowID }) {
            return node
        }
        return nodes.min { $0.order < $1.order }
    }
    private func nextHorizontalKeyboardFocusNode(
        from current: HorizontalKeyboardFocusNode,
        direction: QuickSwitchKeyboardDirection,
        in nodes: [HorizontalKeyboardFocusNode]
    ) -> HorizontalKeyboardFocusNode? {
        let candidates = nodes.filter { !$0.matches(current) }
        guard !candidates.isEmpty else { return current }
        if current.kind == .app,
           direction == .down,
           let firstWindow = firstHorizontalWindowNode(for: current.appGroupIndex, in: nodes) {
            return firstWindow
        }
        if current.kind == .window,
           direction == .up,
           current.windowID == firstHorizontalMasonryCard(for: current.appGroupIndex)?.item.window.id,
           let appNode = nodes.first(where: { $0.kind == .app && $0.appGroupIndex == current.appGroupIndex }) {
            return appNode
        }
        switch direction {
        case .left, .right:
            let sameKindCandidates = candidates.filter { $0.kind == current.kind }
            return nextHorizontalFocusByX(
                from: current,
                direction: direction,
                candidates: sameKindCandidates.isEmpty ? candidates : sameKindCandidates
            )
        case .up, .down:
            return nextHorizontalFocusByY(from: current, direction: direction, candidates: candidates)
        }
    }
    private func firstHorizontalWindowNode(
        for appGroupIndex: Int,
        in nodes: [HorizontalKeyboardFocusNode]
    ) -> HorizontalKeyboardFocusNode? {
        nodes
            .filter { $0.kind == .window && $0.appGroupIndex == appGroupIndex }
            .min { lhs, rhs in
                (lhs.windowIndex ?? .max, lhs.order) < (rhs.windowIndex ?? .max, rhs.order)
            }
    }
    private func nextHorizontalFocusByX(
        from current: HorizontalKeyboardFocusNode,
        direction: QuickSwitchKeyboardDirection,
        candidates: [HorizontalKeyboardFocusNode]
    ) -> HorizontalKeyboardFocusNode? {
        let epsilon: CGFloat = 0.5
        let forward = direction == .right
        let directional = candidates.filter { node in
            forward
                ? node.center.x > current.center.x + epsilon
                : node.center.x < current.center.x - epsilon
        }
        let sameBand = directional.filter { isHorizontalFocusSameBand($0, current) }
        let pool = sameBand.isEmpty ? directional : sameBand
        if let next = pool.sorted(by: horizontalFocusXSort(from: current, forward: forward)).first {
            return next
        }
        let wrapSameBand = candidates.filter { isHorizontalFocusSameBand($0, current) }
        let wrapPool = wrapSameBand.isEmpty ? candidates : wrapSameBand
        return wrapPool.sorted(by: horizontalFocusXWrapSort(from: current, forward: forward)).first
    }
    private func nextHorizontalFocusByY(
        from current: HorizontalKeyboardFocusNode,
        direction: QuickSwitchKeyboardDirection,
        candidates: [HorizontalKeyboardFocusNode]
    ) -> HorizontalKeyboardFocusNode? {
        let epsilon: CGFloat = 0.5
        let upward = direction == .up
        let directional = candidates.filter { node in
            upward
                ? node.center.y > current.center.y + epsilon
                : node.center.y < current.center.y - epsilon
        }
        if let next = directional.sorted(by: horizontalFocusYSort(from: current, upward: upward)).first {
            return next
        }
        return candidates.sorted(by: horizontalFocusYWrapSort(from: current, upward: upward)).first
    }
    private func isHorizontalFocusSameBand(
        _ lhs: HorizontalKeyboardFocusNode,
        _ rhs: HorizontalKeyboardFocusNode
    ) -> Bool {
        let tolerance = max(lhs.frame.height, rhs.frame.height) * 0.75
        return abs(lhs.center.y - rhs.center.y) <= tolerance
    }
    private func horizontalFocusXSort(
        from current: HorizontalKeyboardFocusNode,
        forward: Bool
    ) -> (HorizontalKeyboardFocusNode, HorizontalKeyboardFocusNode) -> Bool {
        { lhs, rhs in
            let lhsPrimary = abs(lhs.center.x - current.center.x)
            let rhsPrimary = abs(rhs.center.x - current.center.x)
            if abs(lhsPrimary - rhsPrimary) > 0.5 {
                return lhsPrimary < rhsPrimary
            }
            let lhsSecondary = abs(lhs.center.y - current.center.y)
            let rhsSecondary = abs(rhs.center.y - current.center.y)
            if abs(lhsSecondary - rhsSecondary) > 0.5 {
                return lhsSecondary < rhsSecondary
            }
            return forward ? lhs.order < rhs.order : lhs.order > rhs.order
        }
    }
    private func horizontalFocusXWrapSort(
        from current: HorizontalKeyboardFocusNode,
        forward: Bool
    ) -> (HorizontalKeyboardFocusNode, HorizontalKeyboardFocusNode) -> Bool {
        { lhs, rhs in
            if abs(lhs.center.x - rhs.center.x) > 0.5 {
                return forward ? lhs.center.x < rhs.center.x : lhs.center.x > rhs.center.x
            }
            let lhsSecondary = abs(lhs.center.y - current.center.y)
            let rhsSecondary = abs(rhs.center.y - current.center.y)
            if abs(lhsSecondary - rhsSecondary) > 0.5 {
                return lhsSecondary < rhsSecondary
            }
            return forward ? lhs.order < rhs.order : lhs.order > rhs.order
        }
    }
    private func horizontalFocusYSort(
        from current: HorizontalKeyboardFocusNode,
        upward: Bool
    ) -> (HorizontalKeyboardFocusNode, HorizontalKeyboardFocusNode) -> Bool {
        { lhs, rhs in
            let lhsPrimary = abs(lhs.center.y - current.center.y)
            let rhsPrimary = abs(rhs.center.y - current.center.y)
            if abs(lhsPrimary - rhsPrimary) > 0.5 {
                return lhsPrimary < rhsPrimary
            }
            let lhsSecondary = abs(lhs.center.x - current.center.x)
            let rhsSecondary = abs(rhs.center.x - current.center.x)
            if abs(lhsSecondary - rhsSecondary) > 0.5 {
                return lhsSecondary < rhsSecondary
            }
            return upward ? lhs.order < rhs.order : lhs.order > rhs.order
        }
    }
    private func horizontalFocusYWrapSort(
        from current: HorizontalKeyboardFocusNode,
        upward: Bool
    ) -> (HorizontalKeyboardFocusNode, HorizontalKeyboardFocusNode) -> Bool {
        { lhs, rhs in
            if abs(lhs.center.y - rhs.center.y) > 0.5 {
                return upward ? lhs.center.y < rhs.center.y : lhs.center.y > rhs.center.y
            }
            let lhsSecondary = abs(lhs.center.x - current.center.x)
            let rhsSecondary = abs(rhs.center.x - current.center.x)
            if abs(lhsSecondary - rhsSecondary) > 0.5 {
                return lhsSecondary < rhsSecondary
            }
            return upward ? lhs.order < rhs.order : lhs.order > rhs.order
        }
    }
    private func focusHorizontalKeyboardNode(
        _ node: HorizontalKeyboardFocusNode,
        direction _: QuickSwitchKeyboardDirection
    ) {
        switch node.kind {
        case .app:
            keyboardFocusedAppGroupIndex = node.appGroupIndex
            keyboardFocusedWindowID = nil
            clearKeyboardBoundaryFeedback()
            ensureAppShelfItemVisible(node.appGroupIndex)
            _ = selectHorizontalMasonryDefaultWindowForApp(
                node.appGroupIndex,
                recordsSelectionChange: true
            )
            hoverAppGroup(node.appGroupIndex, source: .keyboard)
        case .window:
            guard let windowIndex = node.windowIndex,
                  let windowID = node.windowID
            else {
                selectionChangedByLastCommand = false
                return
            }
            let previousSelection = effectiveSelection
            let next = QuickSwitchSelection(
                appGroupIndex: node.appGroupIndex,
                windowIndex: windowIndex,
                windowID: windowID
            )
            keyboardFocusedAppGroupIndex = node.appGroupIndex
            keyboardFocusedWindowID = windowID
            currentSelection = next
            columnSelectionHistory[next.appGroupIndex] = next.windowIndex
            selectionChangedByLastCommand = previousSelection != next
            clearKeyboardBoundaryFeedback()
            ensureAppShelfItemVisible(node.appGroupIndex)
            setHoverTarget(
                .window(appGroupIndex: node.appGroupIndex, windowID: windowID, spaceID: node.spaceID),
                source: .keyboard
            )
            ensureCurrentSelectionVisible(horizontalIntent: .alignWithAppShelf(animated: true))
        }
        needsLayout = true
    }
@@ -2195,7 +3470,7 @@
    }
    private func setupBackdropBlurView() {
        backdropBlurView.material = .fullScreenUI
        backdropBlurView.material = resolvedTheme == .dark ? .hudWindow : .fullScreenUI
        backdropBlurView.blendingMode = .behindWindow
        backdropBlurView.state = .active
        backdropBlurView.isEmphasized = false
@@ -2213,19 +3488,91 @@
        addSubview(layerHostView, positioned: .above, relativeTo: backdropBlurView)
    }
    private func applyThemeToBackdrop() {
        let palette = themePalette
        backdropBlurView.material = resolvedTheme == .dark ? .hudWindow : .fullScreenUI
        backgroundLayer.colors = palette.backgroundGradientColors.map(\.cgColor)
        backgroundLayer.locations = resolvedTheme == .dark
            ? [0, 0.44, 0.72, 1].map(NSNumber.init(value:))
            : [0, 1].map(NSNumber.init(value:))
        safeAreaFillLayer.backgroundColor = palette.safeAreaFill.cgColor
        let showsDarkBackdrop = resolvedTheme == .dark
        darkAmbientLeftLayer.isHidden = !showsDarkBackdrop
        darkAmbientCenterLayer.isHidden = !showsDarkBackdrop
        darkAmbientRightLayer.isHidden = !showsDarkBackdrop
        darkVignetteLayer.isHidden = !showsDarkBackdrop
        guard showsDarkBackdrop else { return }
        darkAmbientLeftLayer.colors = [
            NSColor(calibratedRed: 72 / 255, green: 56 / 255, blue: 145 / 255, alpha: 0.34).cgColor,
            NSColor(calibratedRed: 20 / 255, green: 28 / 255, blue: 76 / 255, alpha: 0.17).cgColor,
            NSColor.clear.cgColor
        ]
        darkAmbientCenterLayer.colors = [
            NSColor(calibratedRed: 14 / 255, green: 92 / 255, blue: 130 / 255, alpha: 0.26).cgColor,
            NSColor(calibratedRed: 12 / 255, green: 54 / 255, blue: 89 / 255, alpha: 0.16).cgColor,
            NSColor.clear.cgColor
        ]
        darkAmbientRightLayer.colors = [
            NSColor(calibratedRed: 4 / 255, green: 92 / 255, blue: 108 / 255, alpha: 0.20).cgColor,
            NSColor(calibratedRed: 0 / 255, green: 44 / 255, blue: 54 / 255, alpha: 0.15).cgColor,
            NSColor.clear.cgColor
        ]
        darkVignetteLayer.colors = [
            NSColor.black.withAlphaComponent(0.48).cgColor,
            NSColor.clear.cgColor,
            NSColor.black.withAlphaComponent(0.28).cgColor
        ]
        darkVignetteLayer.locations = [0, 0.36, 1].map(NSNumber.init(value:))
    }
    private func layoutBackdropLayers() {
        applyThemeToBackdrop()
        guard resolvedTheme == .dark else { return }
        let width = max(1, bounds.width)
        let height = max(1, bounds.height)
        darkAmbientLeftLayer.frame = CGRect(
            x: -width * 0.18,
            y: -height * 0.10,
            width: width * 0.82,
            height: height * 1.24
        )
        darkAmbientCenterLayer.frame = CGRect(
            x: width * 0.24,
            y: height * 0.10,
            width: width * 0.52,
            height: height * 0.74
        )
        darkAmbientRightLayer.frame = CGRect(
            x: width * 0.50,
            y: -height * 0.12,
            width: width * 0.72,
            height: height * 1.26
        )
        darkVignetteLayer.frame = bounds
    }
    private func setupLayers() {
        guard let rootLayer = layerHostView.layer else { return }
        rootLayer.masksToBounds = true
        backgroundLayer.colors = [
            NSColor.windowBackgroundColor.withAlphaComponent(0.58).cgColor,
            NSColor.controlBackgroundColor.withAlphaComponent(0.48).cgColor
        ]
        backgroundLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
        backgroundLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
        rootLayer.addSublayer(backgroundLayer)
        for ambientLayer in darkBackdropAmbientLayers {
            ambientLayer.type = .radial
            ambientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
            ambientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
            ambientLayer.isHidden = true
            rootLayer.addSublayer(ambientLayer)
        }
        darkVignetteLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
        darkVignetteLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
        darkVignetteLayer.isHidden = true
        rootLayer.addSublayer(darkVignetteLayer)
        safeAreaFillLayer.backgroundColor = NSColor.black.cgColor
        rootLayer.addSublayer(safeAreaFillLayer)
        glassLayer.cornerRadius = 0
@@ -2273,25 +3620,27 @@
        closeFeedbackLayer.addSublayer(closeFeedbackTextLayer)
        glassLayer.addSublayer(closeFeedbackLayer)
        settingsButtonLayer.cornerRadius = 10
        settingsButtonLayer.masksToBounds = false
        settingsButtonLayer.borderWidth = 1
        settingsButtonLayer.shadowColor = NSColor.black.cgColor
        settingsButtonLayer.shadowOffset = CGSize(width: 0, height: -2)
        settingsButtonLayer.zPosition = 170
        settingsButtonIconLayer.contentsGravity = .resizeAspect
        settingsButtonIconLayer.contentsScale = backingScaleFactor
        settingsButtonIconLayer.contents = settingsIconImage()
        settingsButtonFallbackTextLayer.contentsScale = backingScaleFactor
        settingsButtonFallbackTextLayer.string = "⚙"
        settingsButtonFallbackTextLayer.font = NSFont.systemFont(ofSize: 18, weight: .regular)
        settingsButtonFallbackTextLayer.fontSize = 18
        settingsButtonFallbackTextLayer.alignmentMode = .center
        settingsButtonFallbackTextLayer.foregroundColor = NSColor.secondaryLabelColor.cgColor
        settingsButtonFallbackTextLayer.isHidden = settingsButtonIconLayer.contents != nil
        settingsButtonLayer.addSublayer(settingsButtonIconLayer)
        settingsButtonLayer.addSublayer(settingsButtonFallbackTextLayer)
        glassLayer.addSublayer(settingsButtonLayer)
        viewModeToggleButtonLayer.cornerRadius = 10
        viewModeToggleButtonLayer.masksToBounds = false
        viewModeToggleButtonLayer.borderWidth = 1
        viewModeToggleButtonLayer.shadowColor = NSColor.black.cgColor
        viewModeToggleButtonLayer.shadowOffset = CGSize(width: 0, height: -2)
        viewModeToggleButtonLayer.zPosition = 170
        viewModeToggleIconLayer.contentsScale = backingScaleFactor
        viewModeToggleButtonLayer.addSublayer(viewModeToggleIconLayer)
        glassLayer.addSublayer(viewModeToggleButtonLayer)
        themeToggleButtonLayer.cornerRadius = 10
        themeToggleButtonLayer.masksToBounds = false
        themeToggleButtonLayer.borderWidth = 1
        themeToggleButtonLayer.shadowColor = NSColor.black.cgColor
        themeToggleButtonLayer.shadowOffset = CGSize(width: 0, height: -2)
        themeToggleButtonLayer.zPosition = 170
        themeToggleIconLayer.contentsScale = backingScaleFactor
        themeToggleButtonLayer.addSublayer(themeToggleIconLayer)
        glassLayer.addSublayer(themeToggleButtonLayer)
        applyThemeToBackdrop()
    }
    private func rebuildAppShelfItems() {
@@ -2299,7 +3648,7 @@
            item.containerLayer.removeFromSuperlayer()
        }
        appShelfItems = (currentViewModel?.appShelf ?? []).map { item in
        appShelfItems = (currentViewModel?.appShelf ?? []).enumerated().map { ordinal, item in
            let container = CALayer()
            container.name = item.app.name
@@ -2316,11 +3665,23 @@
            icon.cornerRadius = 12
            icon.masksToBounds = false
            let indexLabel = makeTextLayer(
                string: appShelfIndexText(forOrdinal: ordinal),
                fontSize: AppShelfMetrics.indexFontSize,
                weight: .regular,
                color: appShelfIndexColor,
                alignment: .center
            )
            indexLabel.font = appShelfIndexFont(size: AppShelfMetrics.indexFontSize)
            indexLabel.fontSize = AppShelfMetrics.indexFontSize
            indexLabel.truncationMode = .end
            indexLabel.zPosition = 14
            let label = makeTextLayer(
                string: item.app.name,
                fontSize: 11,
                weight: .regular,
                color: .secondaryLabelColor,
                color: themePalette.appShelfLabelText,
                alignment: .center
            )
@@ -2340,6 +3701,7 @@
            let closeButton = makeCloseButtonLayers()
            container.addSublayer(background)
            container.addSublayer(indexLabel)
            container.addSublayer(icon)
            container.addSublayer(label)
            container.addSublayer(badge)
@@ -2350,6 +3712,7 @@
                item: item,
                containerLayer: container,
                backgroundLayer: background,
                indexLayer: indexLabel,
                iconLayer: icon,
                labelLayer: label,
                badgeLayer: badge,
@@ -2370,28 +3733,37 @@
            container.name = column.app.name
            container.cornerRadius = 14
            container.masksToBounds = true
            container.backgroundColor = NSColor.controlBackgroundColor.withAlphaComponent(0.56).cgColor
            container.borderColor = NSColor.separatorColor.withAlphaComponent(0.34).cgColor
            container.backgroundColor = themePalette.columnBackground.cgColor
            container.borderColor = themePalette.columnBorder.cgColor
            container.borderWidth = 1
            let cardsClip = CALayer()
            cardsClip.masksToBounds = true
            cardsClip.backgroundColor = NSColor.clear.cgColor
            let header = CALayer()
            header.backgroundColor = NSColor.controlBackgroundColor.withAlphaComponent(0.68).cgColor
            header.backgroundColor = themePalette.columnHeaderBackground.cgColor
            header.zPosition = 40
            let appName = makeTextLayer(
                string: column.app.name,
                fontSize: 13,
                weight: .semibold,
                color: .labelColor,
                color: themePalette.columnText,
                alignment: .center
            )
            let count = makeTextLayer(
                string: "\(column.windows.count)",
                fontSize: 11,
                weight: .medium,
                color: .secondaryLabelColor,
                color: themePalette.columnSecondaryText,
                alignment: .right
            )
            appName.zPosition = 42
            count.zPosition = 42
            container.addSublayer(cardsClip)
            container.addSublayer(header)
            container.addSublayer(appName)
            container.addSublayer(count)
@@ -2416,14 +3788,15 @@
                    string: window.window.title.isEmpty ? "Untitled Window" : window.window.title,
                    fontSize: 12,
                    weight: .medium,
                    color: .labelColor
                    color: themePalette.cardText
                )
                title.truncationMode = .middle
                let meta = makeTextLayer(
                    string: waterfallMetaText(for: window),
                    fontSize: 10,
                    weight: .regular,
                    color: .secondaryLabelColor
                    color: themePalette.cardSecondaryText,
                    alignment: .right
                )
                let appIcon = CALayer()
                appIcon.contentsGravity = .resizeAspect
@@ -2436,13 +3809,13 @@
                    string: waterfallStateText(for: window),
                    fontSize: 10,
                    weight: .medium,
                    color: .secondaryLabelColor,
                    color: themePalette.cardSecondaryText,
                    alignment: .right
                )
                let shine = CAGradientLayer()
                shine.colors = [
                    NSColor.white.withAlphaComponent(0.00).cgColor,
                    NSColor.white.withAlphaComponent(0.16).cgColor,
                    themePalette.shinePeak.cgColor,
                    NSColor.white.withAlphaComponent(0.00).cgColor
                ]
                shine.startPoint = CGPoint(x: 0, y: 1)
@@ -2458,7 +3831,7 @@
                card.addSublayer(state)
                card.addSublayer(shine)
                card.addSublayer(closeButton.containerLayer)
                container.addSublayer(card)
                cardsClip.addSublayer(card)
                return WaterfallCardLayers(
                    item: window,
@@ -2479,6 +3852,7 @@
            return WaterfallColumnLayers(
                column: column,
                containerLayer: container,
                cardsClipLayer: cardsClip,
                headerLayer: header,
                appNameLayer: appName,
                countLayer: count,
@@ -2505,7 +3879,7 @@
            groupContainer.masksToBounds = false
            groupContainer.borderWidth = 0
            groupContainer.borderColor = NSColor.clear.cgColor
            groupContainer.backgroundColor = NSColor.windowBackgroundColor.cgColor
            groupContainer.backgroundColor = themePalette.displayGroupBackground.cgColor
            groupContainer.shadowColor = NSColor.black.cgColor
            groupContainer.shadowOpacity = 0
            groupContainer.shadowRadius = 0
@@ -2526,22 +3900,31 @@
                    string: space.label,
                    fontSize: 15,
                    weight: .semibold,
                    color: .labelColor
                    color: themePalette.spaceLanePrimaryText
                )
                let countLayer = makeTextLayer(
                    string: "\(space.windowCount)",
                    fontSize: 12,
                    weight: .medium,
                    color: .secondaryLabelColor,
                    color: themePalette.spaceLaneSecondaryText,
                    alignment: .center
                )
                let appLayer = makeTextLayer(
                    string: appIdentifierText(for: space),
                    fontSize: 12,
                    weight: space.type == .fullscreen ? .medium : .regular,
                    color: space.type == .fullscreen ? .labelColor : .secondaryLabelColor,
                    color: space.type == .fullscreen ? themePalette.spaceLanePrimaryText : themePalette.spaceLaneSecondaryText,
                    alignment: space.type == .fullscreen ? .center : .left
                )
                let splitAppLayers = splitViewAppNames(for: space).map { name in
                    makeTextLayer(
                        string: name,
                        fontSize: 12,
                        weight: .medium,
                        color: themePalette.spaceLanePrimaryText,
                        alignment: .center
                    )
                }
                let windowBlocks = space.type == .fullscreen
                    ? []
                    : makeWindowBlocks(count: min(space.windowCount, 5), space: space)
@@ -2552,6 +3935,9 @@
                container.addSublayer(appLayer)
                if let fullscreenMarkerLayer {
                    container.addSublayer(fullscreenMarkerLayer)
                }
                for splitAppLayer in splitAppLayers {
                    container.addSublayer(splitAppLayer)
                }
                for block in windowBlocks {
                    container.addSublayer(block)
@@ -2564,6 +3950,7 @@
                    labelLayer: labelLayer,
                    countLayer: countLayer,
                    appLayer: appLayer,
                    splitAppLayers: splitAppLayers,
                    windowBlocks: windowBlocks,
                    fullscreenMarkerLayer: fullscreenMarkerLayer
                )
@@ -2587,6 +3974,7 @@
        CATransaction.setDisableActions(true)
        backgroundLayer.frame = bounds
        layoutBackdropLayers()
        let safeAreaTopInset = topSafeAreaInset
        safeAreaFillLayer.frame = CGRect(
@@ -2629,13 +4017,14 @@
        layoutWaterfallColumns()
        layoutCloseConfirmation()
        layoutCloseFeedback()
        layoutSettingsButton()
        layoutViewModeToggleButton()
        layoutThemeToggleButton()
        CATransaction.commit()
    }
    private func layoutSettingsButton() {
        let size: CGFloat = 34
    private func layoutViewModeToggleButton() {
        let size = ViewModeToggleMetrics.visualSize
        let topMargin = topSafeAreaInset + RootLayoutMetrics.topContentMargin
        let x = max(
            RootLayoutMetrics.horizontalMargin,
@@ -2645,24 +4034,265 @@
            RootLayoutMetrics.bottomMargin,
            glassLayer.bounds.height - topMargin - size
        )
        settingsButtonLayer.frame = CGRect(x: x, y: y, width: size, height: size)
        settingsButtonLayer.backgroundColor = NSColor.windowBackgroundColor
            .withAlphaComponent(isSettingsButtonHovered ? 0.78 : 0.54)
            .cgColor
        settingsButtonLayer.borderColor = NSColor.separatorColor
            .withAlphaComponent(isSettingsButtonHovered ? 0.42 : 0.26)
            .cgColor
        settingsButtonLayer.shadowOpacity = isSettingsButtonHovered ? 0.14 : 0.08
        settingsButtonLayer.shadowRadius = isSettingsButtonHovered ? 9 : 6
        settingsButtonIconLayer.contentsScale = backingScaleFactor
        settingsButtonFallbackTextLayer.contentsScale = backingScaleFactor
        let iconInset: CGFloat = 8
        settingsButtonIconLayer.frame = settingsButtonLayer.bounds.insetBy(dx: iconInset, dy: iconInset)
        settingsButtonFallbackTextLayer.frame = settingsButtonLayer.bounds.insetBy(dx: 0, dy: 6)
        settingsButtonFallbackTextLayer.foregroundColor = (isSettingsButtonHovered
            ? NSColor.labelColor
            : NSColor.secondaryLabelColor
        viewModeToggleButtonLayer.frame = CGRect(x: x, y: y, width: size, height: size)
        viewModeToggleButtonLayer.cornerRadius = ViewModeToggleMetrics.cornerRadius
        let visualState = viewModeToggleVisualState
        let isSelectedState = viewModeToggleVisualStateIsSelected(visualState)
        viewModeToggleButtonLayer.backgroundColor = (isSelectedState
            ? viewModeToggleBlueColor
            : toggleIdleBackgroundColor(hovered: isViewModeToggleButtonHovered)
        ).cgColor
        viewModeToggleButtonLayer.borderColor = (isSelectedState
            ? viewModeToggleBlueColor.withAlphaComponent(0.96)
            : viewModeToggleBlueColor.withAlphaComponent(isViewModeToggleButtonHovered ? 0.72 : 0.34)
        ).cgColor
        viewModeToggleButtonLayer.shadowOpacity = isSelectedState ? 0.16 : (isViewModeToggleButtonHovered ? 0.11 : 0.06)
        viewModeToggleButtonLayer.shadowRadius = isSelectedState
            ? ViewModeToggleMetrics.selectedShadowRadius
            : ViewModeToggleMetrics.idleShadowRadius
        viewModeToggleIconLayer.contentsScale = backingScaleFactor
        let iconInset = ViewModeToggleMetrics.iconInset
        viewModeToggleIconLayer.frame = viewModeToggleButtonLayer.bounds.insetBy(dx: iconInset, dy: iconInset)
        layoutViewModeToggleIcon()
    }
    private func layoutThemeToggleButton() {
        let size = ViewModeToggleMetrics.visualSize
        let topMargin = topSafeAreaInset + RootLayoutMetrics.topContentMargin
        let viewModeFrame = viewModeToggleButtonLayer.frame
        let fallbackX = max(
            RootLayoutMetrics.horizontalMargin,
            glassLayer.bounds.width - RootLayoutMetrics.horizontalMargin - size * 2 - ThemeToggleMetrics.buttonGap
        )
        let x = max(
            RootLayoutMetrics.horizontalMargin,
            (viewModeFrame.width > 0 ? viewModeFrame.minX : fallbackX + size + ThemeToggleMetrics.buttonGap)
                - ThemeToggleMetrics.buttonGap
                - size
        )
        let y = max(
            RootLayoutMetrics.bottomMargin,
            glassLayer.bounds.height - topMargin - size
        )
        themeToggleButtonLayer.frame = CGRect(x: x, y: y, width: size, height: size)
        themeToggleButtonLayer.cornerRadius = ViewModeToggleMetrics.cornerRadius
        let isSelectedState = resolvedTheme == .dark
        themeToggleButtonLayer.backgroundColor = (isSelectedState
            ? viewModeToggleBlueColor
            : themeToggleIdleBackgroundColor
        ).cgColor
        themeToggleButtonLayer.borderColor = (isSelectedState
            ? viewModeToggleBlueColor.withAlphaComponent(0.96)
            : viewModeToggleBlueColor.withAlphaComponent(isThemeToggleButtonHovered ? 0.72 : 0.34)
        ).cgColor
        themeToggleButtonLayer.shadowOpacity = isSelectedState ? 0.18 : (isThemeToggleButtonHovered ? 0.12 : 0.06)
        themeToggleButtonLayer.shadowRadius = isSelectedState
            ? ViewModeToggleMetrics.selectedShadowRadius
            : ViewModeToggleMetrics.idleShadowRadius
        themeToggleIconLayer.contentsScale = backingScaleFactor
        let iconInset = ViewModeToggleMetrics.iconInset
        themeToggleIconLayer.frame = themeToggleButtonLayer.bounds.insetBy(dx: iconInset, dy: iconInset)
        layoutThemeToggleIcon()
    }
    private func layoutViewModeToggleIcon() {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        viewModeToggleIconLayer.sublayers?.forEach { $0.removeFromSuperlayer() }
        let bounds = viewModeToggleIconLayer.bounds
        guard bounds.width > 0, bounds.height > 0 else {
            CATransaction.commit()
            return
        }
        let visualState = viewModeToggleVisualState
        let isSelectedState = viewModeToggleVisualStateIsSelected(visualState)
        let strokeColor = isSelectedState
            ? NSColor.white.withAlphaComponent(0.98)
            : viewModeToggleBlueColor.withAlphaComponent(0.96)
        let fillColor = isSelectedState
            ? NSColor.white.withAlphaComponent(0.22)
            : viewModeToggleBlueColor.withAlphaComponent(0.06)
        let glyphRect = centeredSquare(in: bounds)
        switch visualState {
        case .verticalAvailable, .verticalSelected:
            addVerticalColumnsToggleGlyph(
                in: glyphRect,
                strokeColor: strokeColor,
                fillColor: fillColor
            )
        case .horizontalAvailable, .horizontalSelected:
            addHorizontalMasonryToggleGlyph(
                in: glyphRect,
                strokeColor: strokeColor,
                fillColor: fillColor
            )
        }
        CATransaction.commit()
    }
    private func layoutThemeToggleIcon() {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        themeToggleIconLayer.sublayers?.forEach { $0.removeFromSuperlayer() }
        let bounds = themeToggleIconLayer.bounds
        guard bounds.width > 0, bounds.height > 0 else {
            CATransaction.commit()
            return
        }
        let isSelectedState = resolvedTheme == .dark
        let strokeColor = isSelectedState
            ? NSColor.white.withAlphaComponent(0.98)
            : viewModeToggleBlueColor.withAlphaComponent(0.96)
        let lightFillColor = isSelectedState
            ? NSColor.white.withAlphaComponent(0.22)
            : viewModeToggleBlueColor.withAlphaComponent(0.06)
        let darkFillColor = isSelectedState
            ? NSColor.white.withAlphaComponent(0.96)
            : viewModeToggleBlueColor.withAlphaComponent(0.82)
        let glyphRect = centeredSquare(in: bounds).insetBy(dx: 0.75, dy: 0.75)
        addThemeToggleHalfDiskGlyph(
            in: glyphRect,
            strokeColor: strokeColor,
            lightFillColor: lightFillColor,
            darkFillColor: darkFillColor
        )
        CATransaction.commit()
    }
    private func addThemeToggleHalfDiskGlyph(
        in rect: CGRect,
        strokeColor: NSColor,
        lightFillColor: NSColor,
        darkFillColor: NSColor
    ) {
        let circlePath = CGPath(
            ellipseIn: rect,
            transform: nil
        )
        let baseLayer = CAShapeLayer()
        baseLayer.contentsScale = backingScaleFactor
        baseLayer.frame = themeToggleIconLayer.bounds
        baseLayer.path = circlePath
        baseLayer.fillColor = lightFillColor.cgColor
        baseLayer.strokeColor = NSColor.clear.cgColor
        themeToggleIconLayer.addSublayer(baseLayer)
        let radius = min(rect.width, rect.height) / 2
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let halfPath = CGMutablePath()
        halfPath.move(to: CGPoint(x: center.x, y: center.y + radius))
        halfPath.addArc(
            center: center,
            radius: radius,
            startAngle: .pi / 2,
            endAngle: .pi * 1.5,
            clockwise: false
        )
        halfPath.closeSubpath()
        let halfLayer = CAShapeLayer()
        halfLayer.contentsScale = backingScaleFactor
        halfLayer.frame = themeToggleIconLayer.bounds
        halfLayer.path = halfPath
        halfLayer.fillColor = darkFillColor.cgColor
        halfLayer.strokeColor = NSColor.clear.cgColor
        themeToggleIconLayer.addSublayer(halfLayer)
        let outlineLayer = CAShapeLayer()
        outlineLayer.contentsScale = backingScaleFactor
        outlineLayer.frame = themeToggleIconLayer.bounds
        outlineLayer.path = circlePath
        outlineLayer.fillColor = NSColor.clear.cgColor
        outlineLayer.strokeColor = strokeColor.cgColor
        outlineLayer.lineWidth = ViewModeToggleMetrics.glyphLineWidth
        outlineLayer.lineJoin = .round
        themeToggleIconLayer.addSublayer(outlineLayer)
    }
    private func addVerticalColumnsToggleGlyph(
        in rect: CGRect,
        strokeColor: NSColor,
        fillColor: NSColor
    ) {
        let columnWidth = rect.width * 0.22
        let gap = (rect.width - columnWidth * 3) / 2
        for index in 0..<3 {
            let x = rect.minX + CGFloat(index) * (columnWidth + gap)
            let columnRect = CGRect(x: x, y: rect.minY, width: columnWidth, height: rect.height)
            addViewModeToggleRoundedRect(
                columnRect,
                cornerRadius: ViewModeToggleMetrics.glyphCornerRadius,
                strokeColor: strokeColor,
                fillColor: fillColor,
                lineWidth: ViewModeToggleMetrics.glyphLineWidth
            )
        }
    }
    private func addHorizontalMasonryToggleGlyph(
        in rect: CGRect,
        strokeColor: NSColor,
        fillColor: NSColor
    ) {
        let cellSize = rect.width * 0.38
        let gap = rect.width - cellSize * 2
        for row in 0..<2 {
            for column in 0..<2 {
                let cellRect = CGRect(
                    x: rect.minX + CGFloat(column) * (cellSize + gap),
                    y: rect.minY + CGFloat(row) * (cellSize + gap),
                    width: cellSize,
                    height: cellSize
                )
                addViewModeToggleRoundedRect(
                    cellRect,
                    cornerRadius: ViewModeToggleMetrics.glyphCornerRadius,
                    strokeColor: strokeColor,
                    fillColor: fillColor,
                    lineWidth: ViewModeToggleMetrics.glyphLineWidth
                )
            }
        }
    }
    private func addViewModeToggleRoundedRect(
        _ rect: CGRect,
        cornerRadius: CGFloat,
        strokeColor: NSColor,
        fillColor: NSColor,
        lineWidth: CGFloat
    ) {
        let layer = CAShapeLayer()
        layer.contentsScale = backingScaleFactor
        layer.frame = viewModeToggleIconLayer.bounds
        layer.path = CGPath(
            roundedRect: rect,
            cornerWidth: cornerRadius,
            cornerHeight: cornerRadius,
            transform: nil
        )
        layer.fillColor = fillColor.cgColor
        layer.strokeColor = strokeColor.cgColor
        layer.lineWidth = lineWidth
        layer.lineJoin = .round
        viewModeToggleIconLayer.addSublayer(layer)
    }
    private func centeredSquare(in rect: CGRect) -> CGRect {
        let side = min(rect.width, rect.height)
        return CGRect(
            x: rect.midX - side / 2,
            y: rect.midY - side / 2,
            width: side,
            height: side
        )
    }
    private var topSafeAreaInset: CGFloat {
@@ -2829,7 +4459,7 @@
                || segment.space.id == currentViewModel?.lockedSpaceID
        }
        let hasCurrentSegment = group.display.spaces.contains { $0.isCurrent }
        group.containerLayer.backgroundColor = NSColor.windowBackgroundColor.cgColor
        group.containerLayer.backgroundColor = themePalette.displayGroupBackground.cgColor
        group.containerLayer.borderColor = NSColor.clear.cgColor
        group.containerLayer.borderWidth = 0
        group.containerLayer.shadowOpacity = 0
@@ -2850,20 +4480,25 @@
        let isActive = isHovered || isFocused || isLocked
        let isAssociated = (isAppAssociated || isWindowAssociated) && !isActive
        segment.containerLayer.borderWidth = isLocked
            ? 6
            : (hasWindows || isCurrent || isActive || isAssociated) ? 1.5 : 1
        segment.containerLayer.borderColor = (isActive
        segment.containerLayer.borderWidth = (hasWindows || isCurrent || isActive || isAssociated) ? 1.5 : 1
        segment.containerLayer.borderColor = (isLocked
            ? NSColor.white.withAlphaComponent(0.86)
            : isActive
            ? spaceLaneOccupiedColor.withAlphaComponent(isLocked ? 0.92 : 0.62)
            : isAssociated
                ? spaceLaneOccupiedColor.withAlphaComponent(0.50)
                : hasWindows
                    ? spaceLaneOccupiedColor.withAlphaComponent(isCurrent ? 0.54 : 0.44)
                : isCurrent
                    ? NSColor.labelColor.withAlphaComponent(0.34)
                : NSColor.separatorColor.withAlphaComponent(0.34)
                    ? themePalette.spaceLanePrimaryText.withAlphaComponent(0.34)
                : themePalette.columnBorder.withAlphaComponent(resolvedTheme == .dark ? 0.58 : 0.34)
        ).cgColor
        if !hasWindows {
        if isLocked {
            applySpaceLaneSolidFill(
                to: segment.containerLayer,
                color: spaceLaneLockedFillColor
            )
        } else if !hasWindows {
            applySpaceLaneSolidFill(to: segment.containerLayer, color: .clear)
        } else if isActive {
            applySpaceLaneSolidFill(
@@ -2898,7 +4533,7 @@
        segment.containerLayer.transform = (isActive || isAssociated)
            ? CATransform3DMakeScale(isActive ? 1.03 : 1.015, isActive ? 1.03 : 1.015, 1)
            : CATransform3DIdentity
        segment.containerLayer.shadowPath = hasWindows
        segment.containerLayer.shadowPath = (hasWindows || isLocked)
            ? CGPath(
                roundedRect: segment.containerLayer.bounds,
                cornerWidth: segment.containerLayer.cornerRadius,
@@ -2906,6 +4541,55 @@
                transform: nil
            )
            : nil
        applySpaceLaneContentVisualState(
            segment,
            isLocked: isLocked,
            usesLightActiveFill: resolvedTheme == .dark && hasWindows && !isLocked && (isActive || isAssociated)
        )
    }
    private func applySpaceLaneContentVisualState(
        _ segment: SpaceLaneSegmentLayers,
        isLocked: Bool,
        usesLightActiveFill: Bool
    ) {
        let primaryTextColor: NSColor = isLocked
            ? .white
            : usesLightActiveFill
                ? NSColor.black.withAlphaComponent(0.82)
                : themePalette.spaceLanePrimaryText
        let secondaryTextColor: NSColor = isLocked
            ? .white
            : usesLightActiveFill
                ? NSColor.black.withAlphaComponent(0.58)
                : themePalette.spaceLaneSecondaryText
        let markerTextColor: NSColor = isLocked
            ? .white
            : usesLightActiveFill
                ? NSColor.black.withAlphaComponent(0.62)
                : themePalette.spaceLaneSecondaryText.withAlphaComponent(segment.space.isCurrent ? 0.72 : 0.54)
        let blockColor: NSColor = isLocked
            ? .white
            : usesLightActiveFill
                ? NSColor.black.withAlphaComponent(0.20)
                : spaceLaneOccupiedColor.withAlphaComponent(segment.space.isCurrent ? 0.72 : 0.58)
        segment.labelLayer.foregroundColor = primaryTextColor.cgColor
        segment.countLayer.foregroundColor = secondaryTextColor.cgColor
        segment.appLayer.foregroundColor = (segment.space.type == .fullscreen
            ? primaryTextColor
            : secondaryTextColor
        ).cgColor
        for splitAppLayer in segment.splitAppLayers {
            splitAppLayer.foregroundColor = primaryTextColor.cgColor
        }
        for block in segment.windowBlocks {
            block.backgroundColor = blockColor.cgColor
        }
        (segment.fullscreenMarkerLayer as? CAShapeLayer)?.strokeColor = markerTextColor.cgColor
    }
    private func shouldScrollSpaceLane(for event: NSEvent) -> Bool {
@@ -2978,24 +4662,58 @@
        spaceLaneSegments.first { $0.space.id == spaceID }
    }
    private func settingsButtonContains(windowLocation: CGPoint) -> Bool {
    private func viewModeToggleButtonContains(windowLocation: CGPoint) -> Bool {
        let locationInView = convert(windowLocation, from: nil)
        let locationInGlass = CGPoint(
            x: locationInView.x - glassLayer.frame.minX,
            y: locationInView.y - glassLayer.frame.minY
        )
        return settingsButtonLayer.frame.contains(locationInGlass)
        let hitOutset = (ViewModeToggleMetrics.hitSize - ViewModeToggleMetrics.visualSize) / 2
        return viewModeToggleButtonLayer.frame
            .insetBy(dx: -hitOutset, dy: -hitOutset)
            .contains(locationInGlass)
    }
    private func clickSettingsButton(source: String) {
        DevelopmentDiagnostics.log("quickSwitch.view.clickSettings", [
            "source": source
    private func themeToggleButtonContains(windowLocation: CGPoint) -> Bool {
        let locationInView = convert(windowLocation, from: nil)
        let locationInGlass = CGPoint(
            x: locationInView.x - glassLayer.frame.minX,
            y: locationInView.y - glassLayer.frame.minY
        )
        let hitOutset = (ViewModeToggleMetrics.hitSize - ViewModeToggleMetrics.visualSize) / 2
        return themeToggleButtonLayer.frame
            .insetBy(dx: -hitOutset, dy: -hitOutset)
            .contains(locationInGlass)
    }
    private func clickViewModeToggleButton(source: String) {
        DevelopmentDiagnostics.log("quickSwitch.view.clickWaterfallViewModeToggle", [
            "source": source,
            "currentMode": waterfallViewMode.rawValue,
            "targetMode": nextWaterfallViewMode.rawValue
        ])
        lastMouseCommand = "click-settings"
        if !mouseCommandsApplied.contains("click-settings") {
            mouseCommandsApplied.append("click-settings")
        lastMouseCommand = "click-view-toggle"
        if !mouseCommandsApplied.contains("click-view-toggle") {
            mouseCommandsApplied.append("click-view-toggle")
        }
        onOpenSettings?()
        isViewModeToggleButtonHovered = false
        suppressViewModeToggleHoverPreview = true
        onToggleWaterfallViewMode?()
    }
    private func clickThemeToggleButton(source: String) {
        DevelopmentDiagnostics.log("quickSwitch.view.clickThemeToggle", [
            "source": source,
            "currentTheme": resolvedTheme.rawValue,
            "targetTheme": nextThemePreference.rawValue
        ])
        lastMouseCommand = "click-theme-toggle"
        if !mouseCommandsApplied.contains("click-theme-toggle") {
            mouseCommandsApplied.append("click-theme-toggle")
        }
        isThemeToggleButtonHovered = false
        suppressThemeToggleHoverPreview = true
        onToggleTheme?()
    }
    private func appShelfItem(at windowLocation: CGPoint) -> AppShelfItemLayers? {
@@ -3022,7 +4740,14 @@
                x: locationInWaterfallContent.x - column.containerLayer.frame.minX,
                y: locationInWaterfallContent.y - column.containerLayer.frame.minY
            )
            if let card = column.cards.first(where: { $0.containerLayer.frame.contains(locationInColumn) }) {
            guard column.cardsClipLayer.frame.contains(locationInColumn) else {
                continue
            }
            let locationInCardsClip = CGPoint(
                x: locationInColumn.x - column.cardsClipLayer.frame.minX,
                y: locationInColumn.y - column.cardsClipLayer.frame.minY
            )
            if let card = column.cards.first(where: { $0.containerLayer.frame.contains(locationInCardsClip) }) {
                return card
            }
        }
@@ -3037,23 +4762,70 @@
            .first(where: { $0.item.windowIndex == windowIndex })
    }
    private func waterfallCard(appGroupIndex: Int, windowIndexSymbol: String) -> WaterfallCardLayers? {
        guard let ordinal = Self.appShelfIndexSymbols.firstIndex(of: windowIndexSymbol),
              ordinal < Self.appShelfIndexSymbols.count
        else {
            return nil
        }
        return waterfallCard(appGroupIndex: appGroupIndex, windowIndex: ordinal)
    }
    private func waterfallCard(windowID: UInt32) -> WaterfallCardLayers? {
        waterfallColumns
            .lazy
            .flatMap(\.cards)
            .first(where: { $0.item.window.id == windowID })
    }
    private func layoutSpaceLaneSegmentContents(_ segment: SpaceLaneSegmentLayers) {
        let bounds = segment.containerLayer.bounds
        let splitAppNames = splitViewAppNames(for: segment.space)
        let isSplitView = splitAppNames.count >= 2
        segment.labelLayer.frame = CGRect(x: 10, y: bounds.height - 29, width: max(1, bounds.width - 48), height: 20)
        segment.countLayer.frame = CGRect(x: bounds.width - 34, y: bounds.height - 29, width: 24, height: 20)
        segment.countLayer.string = isSplitView ? "" : "\(segment.space.windowCount)"
        segment.countLayer.isHidden = isSplitView
        if segment.space.type == .fullscreen {
            let markerFrame = fullscreenMarkerFrame(in: bounds)
            segment.appLayer.frame = CGRect(
                x: 8,
                y: markerFrame.midY - 8.5,
                width: max(1, bounds.width - 16),
                height: 17
            )
            segment.appLayer.isHidden = isSplitView
            if isSplitView {
                let labelY = markerFrame.midY - 8.5
                let halfWidth = markerFrame.width / 2
                for (index, splitAppLayer) in segment.splitAppLayers.enumerated() {
                    let x = markerFrame.minX + CGFloat(index) * halfWidth
                    splitAppLayer.isHidden = false
                    splitAppLayer.frame = CGRect(
                        x: x + 4,
                        y: labelY,
                        width: max(1, halfWidth - 8),
                        height: 17
                    )
                }
            } else {
                segment.appLayer.frame = CGRect(
                    x: 8,
                    y: markerFrame.midY - 8.5,
                    width: max(1, bounds.width - 16),
                    height: 17
                )
                for splitAppLayer in segment.splitAppLayers {
                    splitAppLayer.isHidden = true
                }
            }
        } else {
            segment.appLayer.isHidden = false
            segment.appLayer.frame = CGRect(x: 10, y: 9, width: max(1, bounds.width - 20), height: 17)
            for splitAppLayer in segment.splitAppLayers {
                splitAppLayer.isHidden = true
            }
        }
        if let fullscreenMarkerLayer = segment.fullscreenMarkerLayer {
            layoutFullscreenMarkerLayer(fullscreenMarkerLayer, in: bounds)
            layoutFullscreenMarkerLayer(
                fullscreenMarkerLayer,
                in: bounds,
                isSplitView: isSplitView
            )
        }
        let blockGap: CGFloat = 5
@@ -3169,7 +4941,15 @@
            )
            : nil
        item.iconLayer.frame = CGRect(x: iconX, y: iconY, width: visualIconSize, height: visualIconSize)
        layoutAppShelfIndexLayer(
            item.indexLayer,
            iconFrame: item.iconLayer.frame,
            iconSize: visualIconSize,
            itemBounds: bounds,
            highlighted: showsSelectedVisual || isHovered
        )
        item.labelLayer.contentsScale = backingScaleFactor
        item.labelLayer.foregroundColor = themePalette.appShelfLabelText.cgColor
        item.labelLayer.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 14)
        item.badgeLayer.string = badgeText
        item.badgeLayer.isHidden = badgeText.isEmpty
@@ -3194,6 +4974,66 @@
        )
        item.selectedIndicatorLayer.frame = .zero
        item.selectedIndicatorLayer.backgroundColor = NSColor.clear.cgColor
    }
    private var appShelfIndexColor: NSColor {
        themePalette.appShelfIndexText
    }
    private var appShelfIndexHighlightedColor: NSColor {
        themePalette.appShelfIndexHighlightedText
    }
    private func appShelfIndexFont(size: CGFloat) -> NSFont {
        for fontName in ["Bradley Hand ITC", "Noteworthy-Light", "Marker Felt", "Snell Roundhand"] {
            if let font = NSFont(name: fontName, size: size) {
                return font
            }
        }
        return NSFont.monospacedSystemFont(ofSize: size, weight: .regular)
    }
    private func appShelfIndexText(forOrdinal ordinal: Int) -> String {
        guard ordinal >= 0 else { return "" }
        if ordinal < Self.appShelfIndexSymbols.count {
            return Self.appShelfIndexSymbols[ordinal]
        }
        let overflowIndex = ordinal - Self.appShelfIndexSymbols.count
        let prefix = Self.appShelfIndexSymbols[10 + (overflowIndex / 26) % 26]
        let suffix = Self.appShelfIndexSymbols[10 + overflowIndex % 26]
        return "\(prefix)\(suffix)"
    }
    private func layoutAppShelfIndexLayer(
        _ layer: CATextLayer,
        iconFrame: CGRect,
        iconSize: CGFloat,
        itemBounds: CGRect,
        highlighted: Bool
    ) {
        let fontSize = iconSize <= AppShelfMetrics.minIconSize + 1
            ? AppShelfMetrics.compressedIndexFontSize
            : AppShelfMetrics.indexFontSize
        layer.contentsScale = backingScaleFactor
        layer.font = appShelfIndexFont(size: fontSize)
        layer.fontSize = fontSize
        layer.foregroundColor = (highlighted ? appShelfIndexHighlightedColor : appShelfIndexColor).cgColor
        layer.opacity = 1
        let indexWidth = AppShelfMetrics.indexWidth
        let indexHeight = AppShelfMetrics.indexHeight
        let preferredX = iconFrame.minX - indexWidth - 2
        let clampedX = max(-7, min(itemBounds.width - indexWidth, preferredX))
        let preferredY = iconFrame.maxY - indexHeight + 6
        let clampedY = max(0, min(itemBounds.height - indexHeight, preferredY))
        layer.frame = CGRect(
            x: clampedX,
            y: clampedY,
            width: indexWidth,
            height: indexHeight
        )
    }
    private func appShelfRows() -> [[AppShelfItemLayers]] {
@@ -3243,6 +5083,14 @@
                .cards
                .compactMap(\.item.primarySpaceID) ?? []
        )
    }
    private var appIconHoverLinkedAppGroupIndex: Int? {
        if let debugHoveredAppGroupIndex {
            return debugHoveredAppGroupIndex
        }
        guard case .app(let appGroupIndex) = hoverTarget else { return nil }
        return appGroupIndex
    }
    private var appHoverAssociatedSpaceLabels: [String] {
@@ -3358,13 +5206,13 @@
    private func appShelfBackgroundColor(selected: Bool, hovered: Bool, spaceFocused: Bool) -> NSColor {
        if selected {
            return NSColor.controlAccentColor.withAlphaComponent(0.14)
            return themePalette.appShelfBackgroundSelected
        }
        if hovered {
            return NSColor.controlAccentColor.withAlphaComponent(0.13)
            return themePalette.appShelfBackgroundHovered
        }
        if spaceFocused {
            return NSColor.controlAccentColor.withAlphaComponent(0.14)
            return themePalette.appShelfBackgroundSpaceFocused
        }
        return .clear
    }
@@ -3386,6 +5234,10 @@
        }
        return item.item.windowCount > 1 ? "\(item.item.windowCount)" : ""
    }
    private var waterfallAppHoverHeaderFillColor: NSColor {
        themePalette.columnHeaderHoverBackground
    }
    private var appShelfMaxScrollOffset: CGFloat {
@@ -3468,11 +5320,14 @@
            column.containerLayer.shadowOpacity = 0
            column.containerLayer.shadowRadius = 0
            column.containerLayer.shadowOffset = .zero
            column.cardsClipLayer.frame = column.containerLayer.bounds
            column.cardsClipLayer.masksToBounds = false
            column.headerLayer.isHidden = true
            column.appNameLayer.isHidden = true
            column.countLayer.isHidden = true
            for card in column.cards {
                card.containerLayer.transform = CATransform3DIdentity
                if let frame = layout.cardFramesByWindowID[card.item.window.id] {
                    card.containerLayer.frame = frame
                } else {
@@ -3506,14 +5361,14 @@
            1,
            Int(
                floor(
                    (availableWidth + HorizontalMasonryMetrics.cardGap)
                        / (HorizontalMasonryMetrics.cardMinWidth + HorizontalMasonryMetrics.cardGap)
                    (availableWidth + HorizontalMasonryMetrics.horizontalGap)
                        / (HorizontalMasonryMetrics.cardMinWidth + HorizontalMasonryMetrics.horizontalGap)
                )
            )
        )
        let laneCount = min(maximumLaneCount, max(1, cards.count))
        let fittingWidth = (
            availableWidth - CGFloat(max(0, laneCount - 1)) * HorizontalMasonryMetrics.cardGap
            availableWidth - CGFloat(max(0, laneCount - 1)) * HorizontalMasonryMetrics.horizontalGap
        ) / CGFloat(laneCount)
        let cardWidth = floor(
            min(
@@ -3522,7 +5377,7 @@
            )
        )
        let usedWidth = CGFloat(laneCount) * cardWidth
            + CGFloat(max(0, laneCount - 1)) * HorizontalMasonryMetrics.cardGap
            + CGFloat(max(0, laneCount - 1)) * HorizontalMasonryMetrics.horizontalGap
        let startX = max(
            HorizontalMasonryMetrics.horizontalPadding,
            (viewportWidth - usedWidth) / 2
@@ -3538,12 +5393,8 @@
                }
                return lhs.offset < rhs.offset
            }?.offset ?? 0
            let cardHeight = horizontalMasonryCardHeight(
                for: card,
                width: cardWidth,
                viewportHeight: waterfallLayer.bounds.height
            )
            let x = startX + CGFloat(laneIndex) * (cardWidth + HorizontalMasonryMetrics.cardGap)
            let cardHeight = HorizontalMasonryMetrics.cardHeight
            let x = startX + CGFloat(laneIndex) * (cardWidth + HorizontalMasonryMetrics.horizontalGap)
            let top = HorizontalMasonryMetrics.verticalPadding + laneHeights[laneIndex]
            plannedFrames.append((
                windowID: card.item.window.id,
@@ -3552,11 +5403,11 @@
                width: cardWidth,
                height: cardHeight
            ))
            laneHeights[laneIndex] += cardHeight + HorizontalMasonryMetrics.cardGap
            laneHeights[laneIndex] += cardHeight + HorizontalMasonryMetrics.verticalGap
        }
        let tallestLane = laneHeights.max().map {
            max(0, $0 - HorizontalMasonryMetrics.cardGap)
            max(0, $0 - HorizontalMasonryMetrics.verticalGap)
        } ?? 0
        let contentHeight = max(
            waterfallLayer.bounds.height,
@@ -3577,43 +5428,6 @@
        )
        return HorizontalMasonryLayout(contentHeight: contentHeight, cardFramesByWindowID: frames)
    }
    private func horizontalMasonryCardHeight(
        for card: WaterfallCardLayers,
        width: CGFloat,
        viewportHeight: CGFloat
    ) -> CGFloat {
        let thumbnailHeight = min(
            max(
                HorizontalMasonryMetrics.minThumbnailHeight,
                width / horizontalMasonryThumbnailAspectRatio(for: card)
            ),
            max(1, viewportHeight * HorizontalMasonryMetrics.maxThumbnailHeightFraction)
        )
        return HorizontalMasonryMetrics.titleBarHeight
            + HorizontalMasonryMetrics.titleGap
            + thumbnailHeight
    }
    private func horizontalMasonryThumbnailAspectRatio(for card: WaterfallCardLayers) -> CGFloat {
        if card.item.window.isFullscreen {
            return 16.0 / 9.0
        }
        if card.item.window.isMinimized {
            return 4.0 / 3.0
        }
        switch Int(card.item.window.id % 4) {
        case 0:
            return 16.0 / 9.0
        case 1:
            return 3.0 / 2.0
        case 2:
            return 4.0 / 3.0
        default:
            return 5.0 / 4.0
        }
    }
    private func horizontalMasonryContentFrame() -> CGRect {
@@ -3638,7 +5452,7 @@
    private func layoutWaterfallColumn(_ column: WaterfallColumnLayers) {
        column.containerLayer.cornerRadius = 14
        column.containerLayer.masksToBounds = true
        column.containerLayer.backgroundColor = NSColor.controlBackgroundColor.withAlphaComponent(0.56).cgColor
        column.containerLayer.backgroundColor = themePalette.columnBackground.cgColor
        column.containerLayer.shadowOpacity = 0
        column.containerLayer.shadowRadius = 0
        column.containerLayer.shadowOffset = .zero
@@ -3647,11 +5461,19 @@
        column.countLayer.isHidden = false
        let bounds = column.containerLayer.bounds
        let cardsClipFrame = CGRect(
            x: 0,
            y: 0,
            width: bounds.width,
            height: max(1, bounds.height - WaterfallMetrics.headerHeight)
        )
        column.cardsClipLayer.frame = cardsClipFrame
        column.cardsClipLayer.masksToBounds = true
        let isSpaceFocused = spaceFocusWindowCount(for: column.column.appGroupIndex) > 0
        let isHovered = column.column.appGroupIndex == effectiveHoveredAppGroupIndex
        column.containerLayer.borderColor = (isSpaceFocused
            ? NSColor.separatorColor.withAlphaComponent(0.38)
            : NSColor.separatorColor.withAlphaComponent(0.24)
            ? themePalette.columnBorder.withAlphaComponent(resolvedTheme == .dark ? 0.34 : 0.38)
            : themePalette.columnBorder
        ).cgColor
        column.containerLayer.borderWidth = 1
        column.headerLayer.frame = CGRect(
@@ -3660,9 +5482,11 @@
            width: bounds.width,
            height: WaterfallMetrics.headerHeight
        )
        column.headerLayer.backgroundColor = (isSpaceFocused
            ? NSColor.systemFill.withAlphaComponent(0.10)
            : NSColor.controlBackgroundColor.withAlphaComponent(0.52)
        column.headerLayer.backgroundColor = (isHovered
            ? waterfallAppHoverHeaderFillColor
            : (isSpaceFocused
                ? themePalette.columnHeaderSpaceFocusedBackground
                : themePalette.columnHeaderBackground)
        ).cgColor
        column.appNameLayer.frame = CGRect(
            x: WaterfallMetrics.columnHeaderTextInset,
@@ -3670,16 +5494,14 @@
            width: max(1, bounds.width - WaterfallMetrics.columnHeaderTextInset * 2),
            height: 16
        )
        column.appNameLayer.foregroundColor = (isHovered
            ? spaceLaneOccupiedColor
            : NSColor.labelColor
        ).cgColor
        column.appNameLayer.foregroundColor = themePalette.columnText.cgColor
        column.countLayer.frame = CGRect(
            x: bounds.width - 48,
            y: bounds.height - 24,
            width: 36,
            height: 16
        )
        column.countLayer.foregroundColor = themePalette.columnSecondaryText.cgColor
        let appGroupIndex = column.column.appGroupIndex
        let verticalOffset = clampedWaterfallColumnOffset(
@@ -3689,8 +5511,8 @@
        waterfallColumnScrollOffsets[appGroupIndex] = verticalOffset
        let cardWidth = max(1, bounds.width - WaterfallMetrics.columnHorizontalPadding * 2)
        let firstCardTop = bounds.height
            - WaterfallMetrics.headerHeight
        let firstCardTop = cardsClipFrame.height
            - WaterfallMetrics.selectedCardScaleOverflow
            - WaterfallMetrics.columnVerticalPadding
            + verticalOffset
@@ -3699,6 +5521,7 @@
            let y = firstCardTop
                - WaterfallMetrics.cardHeight
                - CGFloat(cardIndex) * (WaterfallMetrics.cardHeight + WaterfallMetrics.cardGap)
            card.containerLayer.transform = CATransform3DIdentity
            card.containerLayer.frame = CGRect(
                x: WaterfallMetrics.columnHorizontalPadding,
                y: y,
@@ -3713,10 +5536,18 @@
        let bounds = card.containerLayer.bounds
        let isSelected = card.item.window.id == effectiveSelection?.windowID
        let isHovered = card.item.window.id == hoveredWindowID
        let isKeyboardFocused = card.item.window.id == keyboardFocusedWindowID
        let isHorizontalAppDefaultFocused = isHorizontalAppDefaultCard(card)
        let isAppLinked = isWaterfallCardAppLinked(card)
        let shortcutFilterState = windowShortcutFilterState(for: card)
        applyWaterfallCardVisualState(
            card,
            selected: shouldShowSelectedVisual(isSelected: isSelected, isHovered: isHovered),
            hovered: isHovered
            selected: shouldShowSelectedVisual(isSelected: isSelected, isHovered: isHovered)
                || isKeyboardFocused
                || isHorizontalAppDefaultFocused,
            hovered: isHovered,
            appLinked: isAppLinked,
            shortcutFilterState: shortcutFilterState
        )
        let outerInset: CGFloat = 0
@@ -3749,18 +5580,21 @@
            width: max(1, titleBarFrame.width - 68),
            height: 16
        )
        card.titleLayer.foregroundColor = themePalette.cardText.cgColor
        card.metaLayer.frame = CGRect(
            x: titleBarFrame.maxX - 38,
            y: titleBarFrame.minY + 6,
            width: 30,
            height: 14
        )
        card.metaLayer.foregroundColor = themePalette.cardSecondaryText.cgColor
        card.stateLayer.frame = CGRect(
            x: thumbnailFrame.maxX - 86,
            y: thumbnailFrame.minY + 6,
            width: 78,
            height: 14
        )
        card.stateLayer.foregroundColor = themePalette.cardSecondaryText.cgColor
        layoutCloseButton(
            card.closeButton,
            target: .window(
@@ -3768,7 +5602,7 @@
                windowIndex: card.item.windowIndex,
                windowID: card.item.window.id
            ),
            visible: isHovered,
            visible: isHovered && shortcutFilterState != .dimmed,
            anchorTopRight: CGPoint(x: bounds.maxX - 12, y: bounds.maxY - 12)
        )
        card.shineLayer.frame = CGRect(
@@ -3777,43 +5611,72 @@
            width: max(36, min(96, bounds.width * 0.24)),
            height: bounds.height
        )
        card.shineLayer.colors = [
            NSColor.white.withAlphaComponent(0.00).cgColor,
            themePalette.shinePeak.cgColor,
            NSColor.white.withAlphaComponent(0.00).cgColor
        ]
    }
    private func applyWaterfallCardVisualState(_ card: WaterfallCardLayers, selected: Bool, hovered: Bool) {
    private func applyWaterfallCardVisualState(
        _ card: WaterfallCardLayers,
        selected: Bool,
        hovered: Bool,
        appLinked: Bool,
        shortcutFilterState: WindowShortcutFilterState
    ) {
        let isSpaceFocused = isWaterfallCardSpaceFocused(card)
        let isShortcutDimmed = shortcutFilterState == .dimmed
        card.containerLayer.backgroundColor = (selected
            ? NSColor.windowBackgroundColor.withAlphaComponent(0.94)
            ? themePalette.cardSelectedBackground
            : hovered
                ? accentTintedWindowBackground(fraction: 0.09, alpha: 0.95)
                : appLinked
                    ? accentTintedWindowBackground(fraction: 0.08, alpha: 0.93)
                : isSpaceFocused
                    ? accentTintedWindowBackground(fraction: 0.10, alpha: 0.94)
                : NSColor.windowBackgroundColor.withAlphaComponent(0.82)
                : themePalette.cardBackground
        ).cgColor
        card.containerLayer.opacity = isShortcutDimmed ? windowShortcutFilterDimOpacity : 1
        card.containerLayer.borderColor = (selected
            ? NSColor.controlAccentColor.withAlphaComponent(0.52)
            : hovered
                ? NSColor.controlAccentColor.withAlphaComponent(0.44)
                : appLinked
                    ? NSColor.controlAccentColor.withAlphaComponent(0.38)
                : isSpaceFocused
                    ? NSColor.controlAccentColor.withAlphaComponent(0.42)
                : NSColor.separatorColor.withAlphaComponent(0.28)
        ).cgColor
        card.containerLayer.borderWidth = (selected || hovered || isSpaceFocused) ? 1.5 : 1
        card.containerLayer.borderWidth = (selected || hovered || appLinked || isSpaceFocused) ? 1.5 : 1
        card.containerLayer.shadowColor = NSColor.black.cgColor
        card.containerLayer.shadowOpacity = selected ? 0.24 : hovered ? 0.14 : isSpaceFocused ? 0.20 : 0
        card.containerLayer.shadowRadius = selected ? 22 : hovered ? 17 : isSpaceFocused ? 24 : 0
        let darkShadowBoost: Float = resolvedTheme == .dark ? 0.08 : 0
        let baseShadowOpacity: Float = selected
            ? 0.24 + darkShadowBoost
            : hovered
                ? 0.14 + darkShadowBoost
                : appLinked
                    ? 0.16 + darkShadowBoost
                    : isSpaceFocused
                        ? 0.20 + darkShadowBoost
                        : 0
        card.containerLayer.shadowOpacity = isShortcutDimmed ? min(baseShadowOpacity, 0.03) : baseShadowOpacity
        card.containerLayer.shadowRadius = selected ? 22 : hovered ? 17 : appLinked ? 18 : isSpaceFocused ? 24 : 0
        card.containerLayer.shadowOffset = selected
            ? CGSize(width: 0, height: -6)
            : hovered
                ? CGSize(width: 0, height: -2.5)
                : appLinked
                    ? CGSize(width: 0, height: -3)
                : isSpaceFocused
                    ? CGSize(width: 0, height: -5)
                    : .zero
        card.containerLayer.zPosition = selected ? 20 : hovered ? 10 : isSpaceFocused ? 8 : 0
        let focusedScale: CGFloat = selected ? 1.015 : isSpaceFocused ? 1.012 : 1.008
        card.containerLayer.transform = (selected || hovered || isSpaceFocused)
        card.containerLayer.zPosition = selected ? 20 : hovered ? 12 : appLinked ? 10 : isSpaceFocused ? 8 : 0
        let focusedScale: CGFloat = selected ? WaterfallMetrics.selectedCardScale : isSpaceFocused ? 1.012 : 1.008
        card.containerLayer.transform = (!isShortcutDimmed && (selected || hovered || isSpaceFocused))
            ? CATransform3DMakeScale(focusedScale, focusedScale, 1)
            : CATransform3DIdentity
        card.containerLayer.shadowPath = (selected || hovered || isSpaceFocused)
        card.containerLayer.shadowPath = (!isShortcutDimmed && (selected || hovered || appLinked || isSpaceFocused))
            ? CGPath(
                roundedRect: card.containerLayer.bounds,
                cornerWidth: card.containerLayer.cornerRadius,
@@ -3821,10 +5684,15 @@
                transform: nil
            )
            : nil
        card.titleBarLayer.backgroundColor = NSColor.clear.cgColor
        card.titleBarLayer.backgroundColor = (selected
            ? spaceLaneOccupiedColor.withAlphaComponent(0.22)
            : appLinked
                ? spaceLaneOccupiedColor.withAlphaComponent(0.18)
                : NSColor.clear
        ).cgColor
        card.titleBarLayer.borderColor = NSColor.clear.cgColor
        card.titleBarLayer.borderWidth = 0
        card.shineLayer.opacity = selected ? 1 : 0
        card.shineLayer.opacity = (!isShortcutDimmed && selected) ? 1 : 0
    }
    private func layoutCloseButton(
@@ -3927,9 +5795,16 @@
        )
        layers.backgroundLayer.frame = layers.containerLayer.bounds
        layers.backgroundLayer.path = bubblePath
        layers.backgroundLayer.fillColor = themePalette.closeFeedbackBackground.withAlphaComponent(resolvedTheme == .dark ? 0.98 : 0.985).cgColor
        layers.backgroundLayer.strokeColor = themePalette.closeFeedbackBorder.withAlphaComponent(resolvedTheme == .dark ? 0.78 : 1).cgColor
        layers.containerLayer.shadowPath = bubblePath
        layers.titleLayer.string = confirmationTitle(for: request)
        layers.messageLayer.string = confirmationMessage(for: request)
        layers.titleLayer.foregroundColor = (resolvedTheme == .dark
            ? NSColor.white.withAlphaComponent(0.92)
            : NSColor.labelColor
        ).cgColor
        layers.messageLayer.foregroundColor = themePalette.closeFeedbackText.cgColor
        layers.titleLayer.frame = CGRect(x: contentFrame.minX + 22, y: height - 48, width: contentFrame.width - 44, height: 22)
        layers.messageLayer.frame = CGRect(x: contentFrame.minX + 22, y: height - 78, width: contentFrame.width - 44, height: 21)
@@ -3940,12 +5815,22 @@
        ).cgColor
        layers.checkboxLayer.borderColor = (pendingCloseOptOut
            ? NSColor.controlAccentColor.withAlphaComponent(0.95)
            : NSColor.separatorColor.withAlphaComponent(0.48)
            : themePalette.closeFeedbackBorder.withAlphaComponent(resolvedTheme == .dark ? 0.80 : 0.48)
        ).cgColor
        layers.checkboxMarkLayer.string = pendingCloseOptOut ? "✓" : ""
        layers.checkboxMarkLayer.frame = CGRect(x: 0, y: 1, width: 18, height: 16)
        layers.checkboxLabelLayer.foregroundColor = themePalette.closeFeedbackText.cgColor
        layers.checkboxLabelLayer.frame = CGRect(x: contentFrame.minX + 48, y: 80, width: contentFrame.width - 70, height: 19)
        layers.cancelButtonLayer.backgroundColor = (resolvedTheme == .dark
            ? NSColor.white.withAlphaComponent(0.10)
            : NSColor.systemFill.withAlphaComponent(0.16)
        ).cgColor
        layers.cancelButtonLayer.borderColor = themePalette.closeFeedbackBorder.withAlphaComponent(resolvedTheme == .dark ? 0.70 : 0.22).cgColor
        layers.cancelButtonTextLayer.foregroundColor = (resolvedTheme == .dark
            ? NSColor.white.withAlphaComponent(0.86)
            : NSColor.labelColor
        ).cgColor
        layers.cancelButtonLayer.frame = CGRect(x: contentFrame.maxX - 164, y: 22, width: 74, height: 32)
        layers.cancelButtonTextLayer.frame = layers.cancelButtonLayer.bounds.insetBy(dx: 0, dy: 7)
        layers.confirmButtonLayer.frame = CGRect(x: contentFrame.maxX - 78, y: 22, width: 56, height: 32)
@@ -3960,9 +5845,9 @@
        closeFeedbackTextLayer.string = feedback.message
        switch feedback.kind {
        case .progress:
            closeFeedbackLayer.backgroundColor = NSColor.windowBackgroundColor.withAlphaComponent(0.94).cgColor
            closeFeedbackLayer.borderColor = NSColor.separatorColor.withAlphaComponent(0.28).cgColor
            closeFeedbackTextLayer.foregroundColor = NSColor.secondaryLabelColor.withAlphaComponent(0.96).cgColor
            closeFeedbackLayer.backgroundColor = themePalette.closeFeedbackBackground.cgColor
            closeFeedbackLayer.borderColor = themePalette.closeFeedbackBorder.cgColor
            closeFeedbackTextLayer.foregroundColor = themePalette.closeFeedbackText.cgColor
        case .failure:
            closeFeedbackLayer.backgroundColor = NSColor.systemRed.withAlphaComponent(0.10).cgColor
            closeFeedbackLayer.borderColor = NSColor.systemRed.withAlphaComponent(0.32).cgColor
@@ -4283,11 +6168,18 @@
                x: locationInWaterfallContent.x - column.containerLayer.frame.minX,
                y: locationInWaterfallContent.y - column.containerLayer.frame.minY
            )
            for card in column.cards where card.containerLayer.frame.contains(locationInColumn) {
            guard column.cardsClipLayer.frame.contains(locationInColumn) else {
                continue
            }
            let locationInCardsClip = CGPoint(
                x: locationInColumn.x - column.cardsClipLayer.frame.minX,
                y: locationInColumn.y - column.cardsClipLayer.frame.minY
            )
            for card in column.cards where card.containerLayer.frame.contains(locationInCardsClip) {
                guard card.closeButton.containerLayer.opacity > 0 else { continue }
                let locationInCard = CGPoint(
                    x: locationInColumn.x - card.containerLayer.frame.minX,
                    y: locationInColumn.y - card.containerLayer.frame.minY
                    x: locationInCardsClip.x - card.containerLayer.frame.minX,
                    y: locationInCardsClip.y - card.containerLayer.frame.minY
                )
                if card.closeButton.containerLayer.frame.insetBy(dx: -8, dy: -8).contains(locationInCard) {
                    return .window(
@@ -4329,11 +6221,13 @@
                x: waterfallLayer.frame.minX
                    + waterfallContentLayer.frame.minX
                    + column.containerLayer.frame.minX
                    + column.cardsClipLayer.frame.minX
                    + card.containerLayer.frame.minX
                    + card.closeButton.containerLayer.frame.midX,
                y: waterfallLayer.frame.minY
                    + waterfallContentLayer.frame.minY
                    + column.containerLayer.frame.minY
                    + column.cardsClipLayer.frame.minY
                    + card.containerLayer.frame.minY
                    + card.closeButton.containerLayer.frame.midY
            )
@@ -4341,14 +6235,39 @@
    }
    private func accentTintedWindowBackground(fraction: CGFloat, alpha: CGFloat) -> NSColor {
        let color = NSColor.windowBackgroundColor.blended(withFraction: fraction, of: NSColor.controlAccentColor)
            ?? NSColor.windowBackgroundColor
        let baseColor = resolvedTheme == .dark ? themePalette.cardBackground : NSColor.windowBackgroundColor
        let color = baseColor.blended(withFraction: fraction, of: NSColor.controlAccentColor)
            ?? baseColor
        return color.withAlphaComponent(alpha)
    }
    private func isWaterfallCardSpaceFocused(_ card: WaterfallCardLayers) -> Bool {
        guard let activeSpaceFocusID else { return false }
        return card.item.primarySpaceID == activeSpaceFocusID
    }
    private func isWaterfallCardAppLinked(_ card: WaterfallCardLayers) -> Bool {
        guard card.item.appGroupIndex == appIconHoverLinkedAppGroupIndex else { return false }
        if isHorizontalMasonryMode {
            return true
        }
        if keyboardFocusedWindowID != nil {
            return false
        }
        return waterfallColumns
            .first(where: { $0.column.appGroupIndex == card.item.appGroupIndex })?
            .cards
            .sorted { lhs, rhs in
                if lhs.item.windowIndex != rhs.item.windowIndex {
                    return lhs.item.windowIndex < rhs.item.windowIndex
                }
                return lhs.item.window.id < rhs.item.window.id
            }
            .first?
            .item.window.id == card.item.window.id
    }
    private func waterfallColumnWidth() -> CGFloat {
@@ -4368,6 +6287,85 @@
    private var isHorizontalMasonryMode: Bool {
        waterfallViewMode == .horizontalMasonry
    }
    private var resolvedTheme: QuickSwitchResolvedTheme {
        themePreference == .dark ? .dark : .light
    }
    private var themePalette: QuickSwitchThemePalette {
        QuickSwitchThemePalette.palette(for: resolvedTheme)
    }
    private var darkBackdropAmbientLayers: [CAGradientLayer] {
        [darkAmbientLeftLayer, darkAmbientCenterLayer, darkAmbientRightLayer]
    }
    private static func normalizedQuickSwitchTheme(_ theme: ThemePreference) -> ThemePreference {
        theme == .dark ? .dark : .light
    }
    private var nextThemePreference: ThemePreference {
        resolvedTheme == .dark ? .light : .dark
    }
    private var nextWaterfallViewMode: QuickSwitchWaterfallViewMode {
        isHorizontalMasonryMode ? .verticalColumns : .horizontalMasonry
    }
    private var viewModeToggleBlueColor: NSColor {
        NSColor(deviceRed: 135.0 / 255.0, green: 200.0 / 255.0, blue: 1, alpha: 1)
    }
    private var themeToggleIdleBackgroundColor: NSColor {
        toggleIdleBackgroundColor(hovered: isThemeToggleButtonHovered)
    }
    private func toggleIdleBackgroundColor(hovered: Bool) -> NSColor {
        switch resolvedTheme {
        case .light:
            return NSColor.windowBackgroundColor.withAlphaComponent(hovered ? 0.76 : 0.58)
        case .dark:
            return NSColor(calibratedRed: 24 / 255, green: 34 / 255, blue: 54 / 255, alpha: hovered ? 0.76 : 0.58)
        }
    }
    private var viewModeToggleVisualState: ViewModeToggleVisualState {
        if isHorizontalMasonryMode {
            return isViewModeToggleButtonHovered ? .verticalAvailable : .horizontalSelected
        }
        return isViewModeToggleButtonHovered ? .horizontalAvailable : .verticalSelected
    }
    private func viewModeToggleVisualStateIsSelected(_ state: ViewModeToggleVisualState) -> Bool {
        switch state {
        case .horizontalSelected, .verticalSelected:
            return true
        case .horizontalAvailable, .verticalAvailable:
            return false
        }
    }
    private var viewModeToggleIconKind: String {
        viewModeToggleVisualState.rawValue
    }
    private var viewModeToggleAccessibilityLabel: String {
        switch nextWaterfallViewMode {
        case .verticalColumns:
            return "切换到纵栏瀑布"
        case .horizontalMasonry:
            return "切换到横栏瀑布"
        }
    }
    private var themeToggleAccessibilityLabel: String {
        switch nextThemePreference {
        case .dark:
            return "切换到深色皮肤"
        case .light, .system:
            return "切换到浅色皮肤"
        }
    }
    private var waterfallMaxScrollOffset: CGFloat {
@@ -4404,7 +6402,7 @@
        )
        let contentHeight = CGFloat(column.cards.count) * WaterfallMetrics.cardHeight
            + CGFloat(max(0, column.cards.count - 1)) * WaterfallMetrics.cardGap
        return max(0, contentHeight - visibleHeight)
        return max(0, contentHeight - visibleHeight + WaterfallMetrics.selectedCardScaleOverflow * 2)
    }
    private func clampedWaterfallColumnOffset(_ offset: CGFloat, appGroupIndex: Int) -> CGFloat {
@@ -4412,7 +6410,130 @@
    }
    private func waterfallMetaText(for card: QuickSwitchWindowCardViewModel) -> String {
        card.primarySpaceLabel ?? "No Space"
        windowShortcutCode(for: card) ?? ""
    }
    private func windowShortcutCode(for card: QuickSwitchWindowCardViewModel) -> String? {
        guard let appOrdinal = appShelfItems.firstIndex(where: { $0.item.appGroupIndex == card.appGroupIndex }),
              let appSymbol = singleIndexSymbol(forOrdinal: appOrdinal),
              let windowSymbol = singleIndexSymbol(forOrdinal: card.windowIndex)
        else {
            return nil
        }
        return "\(appSymbol)\(windowSymbol)"
    }
    private func singleIndexSymbol(forOrdinal ordinal: Int) -> String? {
        guard ordinal >= 0,
              ordinal < Self.appShelfIndexSymbols.count
        else {
            return nil
        }
        return Self.appShelfIndexSymbols[ordinal]
    }
    private var visibleWaterfallCards: [WaterfallCardLayers] {
        waterfallColumns.flatMap(\.cards)
    }
    private func singleVisibleWaterfallCard(appGroupIndex: Int) -> WaterfallCardLayers? {
        let cards = visibleWaterfallCards.filter { $0.item.appGroupIndex == appGroupIndex }
        return cards.count == 1 ? cards[0] : nil
    }
    private var windowShortcutFilterDimOpacity: Float {
        resolvedTheme == .dark ? 0.44 : 0.38
    }
    private func windowShortcutFilterState(for card: WaterfallCardLayers) -> WindowShortcutFilterState {
        switch windowShortcutFilterPhase {
        case .appPending:
            guard let pending = windowIndexKeyPending else { return .none }
            return card.item.appGroupIndex == pending.appGroupIndex ? .matched : .dimmed
        case .targetPreCommit:
            guard let snapshot = lastWindowShortcutPreCommitFilterSnapshot else { return .none }
            if card.item.window.id == snapshot.targetWindowID {
                return .target
            }
            if snapshot.dimmedWindowIDs.contains(card.item.window.id) {
                return .dimmed
            }
            if snapshot.matchedWindowIDs.contains(card.item.window.id) {
                return .matched
            }
            return .none
        case .none, .invalid, .expired:
            return .none
        }
    }
    private func makeWindowShortcutPreCommitFilterSnapshot(
        target card: WaterfallCardLayers,
        code: String
    ) -> WindowShortcutPreCommitFilterSnapshot {
        let targetWindowID = card.item.window.id
        let allWindowIDs = visibleWaterfallCards.map { $0.item.window.id }
        return WindowShortcutPreCommitFilterSnapshot(
            targetCode: code,
            targetWindowID: targetWindowID,
            matchedWindowIDs: [targetWindowID],
            dimmedWindowIDs: allWindowIDs.filter { $0 != targetWindowID }.sorted()
        )
    }
    private func makeWindowShortcutFilterReport() -> WindowShortcutFilterReport {
        switch windowShortcutFilterPhase {
        case .appPending:
            guard let pending = windowIndexKeyPending else {
                return emptyWindowShortcutFilterReport(phase: .none)
            }
            let cards = visibleWaterfallCards
            let matched = cards
                .filter { $0.item.appGroupIndex == pending.appGroupIndex }
                .map { $0.item.window.id }
                .sorted()
            let dimmed = cards
                .filter { $0.item.appGroupIndex != pending.appGroupIndex }
                .map { $0.item.window.id }
                .sorted()
            return WindowShortcutFilterReport(
                active: true,
                phase: .appPending,
                pendingAppGroupIndex: pending.appGroupIndex,
                prefix: pending.appSymbol,
                matchedWindowIDs: matched,
                dimmedWindowIDs: dimmed
            )
        case .targetPreCommit:
            guard let snapshot = lastWindowShortcutPreCommitFilterSnapshot else {
                return emptyWindowShortcutFilterReport(phase: .none)
            }
            return WindowShortcutFilterReport(
                active: true,
                phase: .targetPreCommit,
                pendingAppGroupIndex: nil,
                prefix: snapshot.targetCode,
                matchedWindowIDs: snapshot.matchedWindowIDs,
                dimmedWindowIDs: snapshot.dimmedWindowIDs
            )
        case .invalid, .expired:
            return emptyWindowShortcutFilterReport(phase: windowShortcutFilterPhase)
        case .none:
            return emptyWindowShortcutFilterReport(phase: .none)
        }
    }
    private func emptyWindowShortcutFilterReport(
        phase: WindowShortcutFilterPhase
    ) -> WindowShortcutFilterReport {
        WindowShortcutFilterReport(
            active: false,
            phase: phase,
            pendingAppGroupIndex: nil,
            prefix: nil,
            matchedWindowIDs: [],
            dimmedWindowIDs: []
        )
    }
    private func waterfallStateText(for card: QuickSwitchWindowCardViewModel) -> String {
@@ -4431,17 +6552,25 @@
    }
    private var spaceLaneOccupiedColor: NSColor {
        if effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua {
        if resolvedTheme == .dark {
            return NSColor(calibratedRed: 10 / 255, green: 132 / 255, blue: 1, alpha: 1)
        }
        return NSColor(calibratedRed: 0, green: 122 / 255, blue: 1, alpha: 1)
    }
    private var spaceLaneLockedFillColor: NSColor {
        if resolvedTheme == .dark {
            return NSColor(calibratedRed: 58 / 255, green: 148 / 255, blue: 1, alpha: 0.86)
        }
        return NSColor(calibratedRed: 90 / 255, green: 171 / 255, blue: 1, alpha: 0.82)
    }
    private func spaceLaneIdleOccupiedGradientColors() -> [NSColor] {
        if effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua {
            let blue = NSColor(calibratedRed: 36 / 255, green: 54 / 255, blue: 72 / 255, alpha: 1)
            let surface = NSColor.windowBackgroundColor.withAlphaComponent(1)
        if resolvedTheme == .dark {
            let blue = NSColor(calibratedRed: 18 / 255, green: 42 / 255, blue: 70 / 255, alpha: 0.92)
            let surface = NSColor(calibratedRed: 22 / 255, green: 34 / 255, blue: 50 / 255, alpha: 0.78)
            return [blue, surface]
        }
@@ -4454,7 +6583,7 @@
    private func applySpaceLaneGradientFill(to layer: CALayer, colors: [NSColor]) {
        if let gradientLayer = layer as? CAGradientLayer {
            gradientLayer.colors = colors.map(\.cgColor)
            gradientLayer.locations = [0, 1]
            gradientLayer.locations = [0, 1].map(NSNumber.init(value:))
            gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
            gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
        }
@@ -4479,21 +6608,6 @@
        shapeLayer.lineCap = .round
        shapeLayer.lineJoin = .round
        return shapeLayer
    }
    private func settingsIconImage() -> CGImage? {
        let names = [
            NSImage.Name("NSPreferencesGeneral"),
            NSImage.Name("NSAdvanced")
        ]
        for name in names {
            guard let image = NSImage(named: name) else { continue }
            var rect = CGRect(origin: .zero, size: image.size)
            if let cgImage = image.cgImage(forProposedRect: &rect, context: nil, hints: nil) {
                return cgImage
            }
        }
        return nil
    }
    private func makeTextLayer(
@@ -4673,13 +6787,38 @@
        return identifiers.joined(separator: " ")
    }
    private func layoutFullscreenMarkerLayer(_ layer: CALayer, in segmentBounds: CGRect) {
    private func splitViewAppNames(for space: QuickSwitchSpaceViewModel) -> [String] {
        guard space.type == .fullscreen else { return [] }
        let names = space.splitViewAppNames
            .prefix(2)
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty }
        guard names.count >= 2 else { return [] }
        return names
    }
    private func layoutFullscreenMarkerLayer(
        _ layer: CALayer,
        in segmentBounds: CGRect,
        isSplitView: Bool = false
    ) {
        let markerFrame = fullscreenMarkerFrame(in: segmentBounds)
        layer.frame = markerFrame
        guard let shapeLayer = layer as? CAShapeLayer else { return }
        let bounds = shapeLayer.bounds
        if isSplitView {
            let path = CGMutablePath()
            path.move(to: CGPoint(x: bounds.midX, y: bounds.minY))
            path.addLine(to: CGPoint(x: bounds.midX, y: bounds.maxY))
            shapeLayer.path = path
            shapeLayer.lineCap = .butt
            return
        }
        let cornerLength = min(12, max(7, min(bounds.width, bounds.height) * 0.28))
        let path = CGMutablePath()
@@ -4700,6 +6839,7 @@
        path.addLine(to: CGPoint(x: 0, y: bounds.maxY - cornerLength))
        shapeLayer.path = path
        shapeLayer.lineCap = .round
    }
    private func fullscreenMarkerFrame(in segmentBounds: CGRect) -> CGRect {
@@ -4749,6 +6889,12 @@
                "frame": dictionary(from: item.containerLayer.frame),
                "visibleFrame": dictionary(from: visibleFrame),
                "iconFrame": dictionary(from: item.iconLayer.frame),
                "indexText": item.indexLayer.string ?? "",
                "indexFrame": dictionary(from: item.indexLayer.frame),
                "indexVisible": !item.indexLayer.isHidden && item.indexLayer.opacity > 0,
                "indexFontSize": Double(item.indexLayer.fontSize),
                "indexOpacity": Double(item.indexLayer.opacity),
                "indexColor": colorDictionary(from: item.indexLayer.foregroundColor),
                "labelFrame": dictionary(from: item.labelLayer.frame),
                "labelTruncationMode": "middle",
                "badgeVisible": !item.badgeLayer.isHidden,
@@ -4802,10 +6948,13 @@
            let appGroupIndex = column.column.appGroupIndex
            let isHovered = appGroupIndex == effectiveHoveredAppGroupIndex
            let firstCardFrame = column.cards.first?.containerLayer.frame
            let headerToFirstCardGap = firstCardFrame.map {
            let visibleFirstCardFrame = firstCardFrame.map {
                $0.offsetBy(dx: column.cardsClipLayer.frame.minX, dy: column.cardsClipLayer.frame.minY)
            }
            let headerToFirstCardGap = visibleFirstCardFrame.map {
                column.headerLayer.frame.minY - $0.maxY
            }
            let topToFirstCardGap = firstCardFrame.map {
            let topToFirstCardGap = visibleFirstCardFrame.map {
                column.containerLayer.bounds.height - $0.maxY
            }
@@ -4822,12 +6971,16 @@
                "frame": dictionary(from: column.containerLayer.frame),
                "visibleFrame": dictionary(from: visibleFrame),
                "headerFrame": dictionary(from: column.headerLayer.frame),
                "headerBackgroundColor": colorDictionary(from: column.headerLayer.backgroundColor),
                "cardsClipFrame": dictionary(from: column.cardsClipLayer.frame),
                "cardsClipMasksToBounds": column.cardsClipLayer.masksToBounds,
                "headerToFirstCardGap": headerToFirstCardGap.map(Double.init) ?? NSNull(),
                "topToFirstCardGap": topToFirstCardGap.map(Double.init) ?? NSNull(),
                "appNameFrame": dictionary(from: column.appNameLayer.frame),
                "appNameAlignment": column.appNameLayer.alignmentMode.rawValue,
                "appNameColor": colorDictionary(from: column.appNameLayer.foregroundColor),
                "countFrame": dictionary(from: column.countLayer.frame),
                "countColor": colorDictionary(from: column.countLayer.foregroundColor),
                "verticalScrollOffset": Double(waterfallColumnScrollOffsets[appGroupIndex] ?? 0),
                "maxVerticalScrollOffset": Double(waterfallMaxVerticalScrollOffset(for: appGroupIndex)),
                "cards": waterfallCardReports(for: column)
@@ -4885,10 +7038,14 @@
    private func waterfallCardReports(for column: WaterfallColumnLayers) -> [[String: Any]] {
        column.cards.map { card in
            let visibleFrame = card.containerLayer.frame.offsetBy(
                dx: column.containerLayer.frame.minX + waterfallContentLayer.frame.minX,
                dy: column.containerLayer.frame.minY
                dx: column.containerLayer.frame.minX + column.cardsClipLayer.frame.minX + waterfallContentLayer.frame.minX,
                dy: column.containerLayer.frame.minY + column.cardsClipLayer.frame.minY
            )
            let screenshotSource = screenshotSourcesByWindowID[card.item.window.id]
            let shortcutFilterState = windowShortcutFilterState(for: card)
            let shortcutFilterOpacity = shortcutFilterState == .dimmed
                ? windowShortcutFilterDimOpacity
                : Float(1)
            var report = [
                "appGroupIndex": card.item.appGroupIndex,
@@ -4900,10 +7057,15 @@
                "titleIsEmpty": card.item.window.title.isEmpty,
                "primarySpaceID": card.item.primarySpaceID ?? NSNull(),
                "primarySpaceLabel": card.item.primarySpaceLabel ?? NSNull(),
                "windowShortcutCode": waterfallMetaText(for: card.item),
                "isMinimized": card.item.window.isMinimized,
                "isFullscreen": card.item.window.isFullscreen,
                "identifierSource": "\(card.item.window.identifierSource)",
                "isSelected": card.item.window.id == effectiveSelection?.windowID,
                "isKeyboardFocused": card.item.window.id == keyboardFocusedWindowID,
                "isHorizontalAppDefault": isHorizontalAppDefaultCard(card),
                "windowShortcutFilterState": shortcutFilterState.rawValue,
                "windowShortcutFilterOpacity": Double(shortcutFilterOpacity),
                "visualStates": waterfallCardVisualStates(for: card),
                "thumbnailStrategy": thumbnailStrategyString(for: card.item.window),
                "screenshotSource": screenshotSourceString(screenshotSource, for: card.item.window),
@@ -4914,6 +7076,7 @@
                "shadowRadius": Double(card.containerLayer.shadowRadius),
                "thumbnailFrame": dictionary(from: card.thumbnailLayer.frame),
                "titleBarFrame": dictionary(from: card.titleBarLayer.frame),
                "titleBarBackgroundColor": colorDictionary(from: card.titleBarLayer.backgroundColor),
                "titleBarBorderWidth": Double(card.titleBarLayer.borderWidth),
                "titleFrame": dictionary(from: card.titleLayer.frame),
                "metaFrame": dictionary(from: card.metaLayer.frame),
@@ -4927,6 +7090,7 @@
                "closeButtonGlyphLineWidth": Double(card.closeButton.glyphLayer.lineWidth),
                "shineVisible": card.shineLayer.opacity > 0,
                "zPosition": Double(card.containerLayer.zPosition),
                "bounds": dictionary(from: card.containerLayer.bounds),
                "frame": dictionary(from: card.containerLayer.frame),
                "visibleFrame": dictionary(from: visibleFrame),
                "titleTruncationMode": "middle"
@@ -5021,16 +7185,40 @@
        var states: [String] = []
        let isSelected = card.item.window.id == effectiveSelection?.windowID
        let isHovered = card.item.window.id == hoveredWindowID
        if shouldShowSelectedVisual(isSelected: isSelected, isHovered: isHovered) {
        let isKeyboardFocused = card.item.window.id == keyboardFocusedWindowID
        let isHorizontalAppDefaultFocused = isHorizontalAppDefaultCard(card)
        let isAppLinked = isWaterfallCardAppLinked(card)
        if shouldShowSelectedVisual(isSelected: isSelected, isHovered: isHovered)
            || isKeyboardFocused
            || isHorizontalAppDefaultFocused {
            states.append("selected")
        } else if isSelected {
            states.append("selectedVisualSuppressed")
        }
        if isKeyboardFocused {
            states.append("keyboardFocused")
        }
        if isHorizontalAppDefaultFocused {
            states.append("appDefault")
        }
        if isHovered {
            states.append("hover")
        }
        if isAppLinked {
            states.append("appLinked")
        }
        if isWaterfallCardSpaceFocused(card) {
            states.append("spaceFocused")
        }
        switch windowShortcutFilterState(for: card) {
        case .matched:
            states.append("shortcutMatched")
        case .dimmed:
            states.append("shortcutDimmed")
        case .target:
            states.append("shortcutTarget")
        case .none:
            break
        }
        if card.item.window.isMinimized {
            states.append("minimized")
@@ -5079,6 +7267,10 @@
                }
                let isAppAssociated = appHoverAssociatedSpaceIDs.contains(space.id)
                let isWindowAssociated = space.id == windowHoverAssociatedSpaceID
                let isLocked = space.id == currentViewModel?.lockedSpaceID
                let isActive = space.id == activeSpaceFocusID || isLocked
                let isAssociated = (isAppAssociated || isWindowAssociated) && !isActive
                let usesLightActiveFill = resolvedTheme == .dark && space.windowCount > 0 && !isLocked && (isActive || isAssociated)
                let group = spaceLaneDisplayGroup(containingSpaceID: space.id)
                let visibleFrame = segment.containerLayer.frame.offsetBy(
                    dx: (group?.containerLayer.frame.minX ?? 0) + spaceLaneContentLayer.frame.minX,
@@ -5088,6 +7280,8 @@
                let fullscreenMarkerCenterRatio = fullscreenMarkerFrame.map {
                    Double($0.midY / max(1, segment.containerLayer.bounds.height))
                }
                let splitAppNames = splitViewAppNames(for: space)
                let splitAppLabelFrames = segment.splitAppLayers.map { dictionary(from: $0.frame) }
                return [
                    "displayUUID": display.displayUUID,
                    "displayLabel": display.displayLabel,
@@ -5095,13 +7289,35 @@
                    "label": space.label,
                    "type": spaceTypeName(space.type),
                    "visualStates": visualStates(for: space),
                    "usesLightActiveFill": usesLightActiveFill,
                    "foregroundContrastStyle": usesLightActiveFill ? "darkOnLight" : "themeDefault",
                    "fullscreenMarkerVisible": segment.fullscreenMarkerLayer != nil,
                    "fullscreenMarkerKind": segment.fullscreenMarkerLayer == nil ? "none" : "cornerBracket",
                    "fullscreenMarkerKind": fullscreenMarkerKind(
                        markerLayer: segment.fullscreenMarkerLayer,
                        splitAppNames: splitAppNames
                    ),
                    "fullscreenMarkerPathStyle": fullscreenMarkerPathStyle(
                        markerLayer: segment.fullscreenMarkerLayer,
                        splitAppNames: splitAppNames
                    ),
                    "fullscreenMarkerFrame": fullscreenMarkerFrame.map { dictionary(from: $0) } ?? NSNull(),
                    "fullscreenMarkerCenterRatio": fullscreenMarkerCenterRatio ?? NSNull(),
                    "splitViewAppNames": splitAppNames,
                    "splitViewLabelFrames": splitAppLabelFrames,
                    "splitViewLabelColors": segment.splitAppLayers.map {
                        colorDictionary(from: $0.foregroundColor)
                    },
                    "windowBlockCount": segment.windowBlocks.count,
                    "windowBlockColors": segment.windowBlocks.map { colorDictionary(from: $0.backgroundColor) },
                    "windowBlockFrames": segment.windowBlocks.map { dictionary(from: $0.frame) },
                    "labelColor": colorDictionary(from: segment.labelLayer.foregroundColor),
                    "countText": string(from: segment.countLayer.string),
                    "countVisible": !segment.countLayer.isHidden && segment.countLayer.opacity > 0,
                    "countColor": colorDictionary(from: segment.countLayer.foregroundColor),
                    "appColor": colorDictionary(from: segment.appLayer.foregroundColor),
                    "fullscreenMarkerStrokeColor": colorDictionary(
                        from: (segment.fullscreenMarkerLayer as? CAShapeLayer)?.strokeColor
                    ),
                    "backgroundKind": spaceLaneFillKind(for: segment.containerLayer),
                    "backgroundColor": colorDictionary(from: segment.containerLayer.backgroundColor),
                    "gradientColors": gradientColorDictionaries(from: segment.containerLayer),
@@ -5136,6 +7352,16 @@
        }
    }
    private func fullscreenMarkerKind(markerLayer: CALayer?, splitAppNames: [String]) -> String {
        guard markerLayer != nil else { return "none" }
        return splitAppNames.count >= 2 ? "splitViewPair" : "cornerBracket"
    }
    private func fullscreenMarkerPathStyle(markerLayer: CALayer?, splitAppNames: [String]) -> String {
        guard markerLayer != nil else { return "none" }
        return splitAppNames.count >= 2 ? "centerDividerOnly" : "cornerBracket"
    }
    private func visualStates(for space: QuickSwitchSpaceViewModel) -> [String] {
        var states: [String] = []
        if space.isCurrent {
@@ -5158,6 +7384,9 @@
        }
        if space.type == .fullscreen {
            states.append("fullscreen")
        }
        if splitViewAppNames(for: space).count >= 2 {
            states.append("splitView")
        }
        return states
    }
@@ -5182,6 +7411,16 @@
            "width": Double(rect.width),
            "height": Double(rect.height)
        ]
    }
    private func string(from textLayerValue: Any?) -> String {
        if let string = textLayerValue as? String {
            return string
        }
        if let attributedString = textLayerValue as? NSAttributedString {
            return attributedString.string
        }
        return ""
    }
    private func colorDictionary(from color: CGColor?) -> [String: Double] {
@@ -5212,6 +7451,20 @@
        }
    }
    private var darkBackdropLayerCount: Int {
        darkBackdropAmbientLayers.count + 1
    }
    private var darkBackdropLayersVisible: Bool {
        resolvedTheme == .dark
            && darkBackdropAmbientLayers.allSatisfy { !$0.isHidden }
            && !darkVignetteLayer.isHidden
    }
    private func backgroundGradientColorReports() -> [[String: Double]] {
        gradientColorDictionaries(from: backgroundLayer)
    }
    private func spaceLaneFillKind(for layer: CALayer) -> String {
        if !gradientColorDictionaries(from: layer).isEmpty {
            return "gradient"