<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="using:System.Collections.Generic"
        xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
        xmlns:views="using:DodoSSH.Client.App.Views"
        x:Class="DodoSSH.Client.App.Views.MainWindow"
        x:DataType="vm:MainWindowViewModel"
        Title="DodoSSH"
        Icon="/Assets/dodossh.ico"
        Width="1180"
        Height="760"
        MinWidth="1081"
        MinHeight="583"
        Background="{StaticResource Canvas}"
        SystemDecorations="BorderOnly"
        Focusable="True">

  <!--
    The shell window: a titlebar it draws itself, a nav rail, one surface at a time, and a status bar.

    THE MINIMUM GREW, and it grew by exactly what v2 added rather than by a round number somebody liked.
    The sidebar went from 54 pixels to 190 and the chrome from 72 tall to 86, so 880x560 became 1016x574 —
    which leaves every screen the same 826x464 it was designed against. That is the whole point of moving
    it: the layout suite's premise is that everything fits at the minimum, and four of the tables stop
    fitting at 690 wide. Widening the sidebar without widening the window would have quietly broken them
    somewhere nobody was looking.

    v5b moves it again, by exactly the same reasoning: the titlebar's fidelity pass takes it from 44 to 53
    and the rail's from 190 to 255, so 1016x574 becomes 1081x583 — nine pixels and sixty-five pixels, added
    straight onto the minimum rather than absorbed by shrinking a screen. <c>LayoutHarness.ScreenWidth</c>
    stays unchanged at 826, because the rail is the only thing beside a page that grew.

    <c>ScreenHeight</c> did move, and in the other direction: v5b's own fidelity pass also retires the
    window-wide tab strip this comment used to describe — see the paragraph below — and every full-bleed
    page gets that 42 pixels back rather than the window losing height to compensate. A screen this suite
    measures is 826 pixels wide and taller than it was, by exactly the strip's own height; see
    <c>LayoutHarness</c>'s own remark on the budget for the arithmetic.

    Windows is asked for a resize border and nothing else, so TitleBar does the dragging, the maximising and
    the closing. That is a real cost, and the reason it is paid is that a stock grey system bar above a
    near-black application is the one part of the window that would look borrowed.

    SystemDecorations="BorderOnly" rather than extending the client area under the system chrome, which is
    what this tried first and is measurably wrong on Avalonia 12: ExtendClientAreaChromeHints is gone from
    that version, so extending leaves the caption buttons and the window title drawn on top of this
    window's own — two sets of minimise/maximise/close, and "DodoSSH" printed twice, overlapping. BorderOnly
    removes the caption and keeps the resize border and the drop shadow, which is the half of the system
    chrome worth having.

    TWO SURFACES, ONE RECTANGLE — AND EACH ONE OWNS ITS OWN TAB ROW NOW.

    v5b retires the tab strip this file used to draw above the whole window — Vaults, SFTP and S3 left it
    for the rail's own switcher in an earlier pass, and this one moves the remaining pills, one per open
    terminal, off the window's own chrome entirely. Each of the two screens that carries a tab row —
    <c>SessionTabRow.axaml</c> — draws its own, 38 pixels, inside its own 26-pixel padded column, per the
    design; see the terminal surface's own Grid below and the SFTP one inside the pages Panel. A tab still
    survives navigating away from either screen — that is what makes the terminal reachable from anywhere —
    it simply is not drawn as chrome above every screen while it does.

    The terminal and the pages still share the one rectangle beside the rail, and exactly one of them may
    occupy it at a time: that is the whole of ShellSurface, an enum rather than two flags, so there is no way
    to write the state where both are showing.

    THE OCCLUSION RULE, which every arrangement in this file obeys.

    NativeWebView hosts a real Win32 child window through NativeControlHost, and a child window composites
    above everything the parent paints — so no sibling in this visual tree can cover it, whatever the
    z-order says. Layering a screen over it leaves that screen sliced at the WebView's left edge with its
    buttons unreachable, which this window has shipped once already.

    So anything that would occupy the terminal's rectangle collapses the terminal instead, and
    IsTerminalShowing is the one place that decision is made: a locked vault, the page area, or the
    quick-connect palette. Collapsing is safe, and cheaply so — NativeControlHost creates the native
    control when the control is attached to the visual tree, not when it is laid out or shown, so WebView2
    still starts, still loads the page and still lets the renderer attach its socket while it is false. It
    only swaps ShowInBounds for HideWithSize, and flipping it back re-pushes the bounds.

    Note where IsShowingPages is bound: on the one Panel that holds every screen, not on each screen. That
    is what makes the rule hard to break rather than merely documented — a sixth screen added inside that
    Panel cannot forget to collapse, because it is not the thing doing the collapsing. Its own IsVisible
    only chooses between the pages.

    Two nearby alternatives are wrong. Removing the control from the tree instead — conditional content, a
    template swap — detaches it, and detaching destroys the native control and the whole WebView2 process
    tree, so every unlock would pay a cold start. Hoisting the binding to an ancestor looks tidier and is
    unverified: NativeControlHost does watch ancestors, but NativeWebView's own bounds-and-scaling re-push
    fires only for its own IsVisible. v5b nests the WebView three levels deeper than it used to sit, inside
    the terminal surface's own session shell — see that Grid's own remark below for why the rule still holds
    with the control that much further from the Panel that used to be its only parent.
  -->

  <Grid RowDefinitions="Auto,*,Auto,Auto">

    <views:TitleBar Grid.Row="0" IsVisible="{Binding !IsSettingsMode}" />

    <Panel Grid.Row="1">

      <!-- The unlocked application. -->
      <Grid ColumnDefinitions="Auto,*" IsVisible="{Binding IsUnlocked}"
            IsEnabled="{Binding !IsSettingsMode}">

        <!--
          The window's own furniture, at 255 pixels — see NavRail.axaml's own v5b remark. It no longer sits
          under a strip: v5b retired the window-wide tab strip entirely, and with it the row this Grid used
          to give up its own first row to. See the remark below on where a session's tabs live now.
        -->
        <views:NavRail Grid.Column="0" />

        <Panel Grid.Column="1">

          <!-- ============ THE PAGES ============ -->
          <Panel IsVisible="{Binding IsShowingPages}">

            <!--
              Wrapped, like every other screen here. It used to be bound directly — its data context was
              the shell's, so IsHostsScreen resolved on the same element — and that was only so it could
              hand the vault to the host sidebar from inside its own markup. The sidebar is gone and the
              drawer that replaced it is a plain child, so the screen takes the vault like the rest and
              its visibility goes on the wrapper.
            -->
            <Panel IsVisible="{Binding IsHostsScreen}">
              <views:HostsScreen x:Name="HostsPane" DataContext="{Binding Vault}" />
            </Panel>

            <!-- ============ SFTP ============ -->
            <!--
              ◆ v5b's session shell, wrapping this screen's existing content rather than replacing it — the
              two-pane grid and the queue inside TransfersScreen.axaml are untouched; wave C restyles their
              internals. What is new here is everything design-notes/v5b-fidelity-notes.md calls the session
              shell: a 26px padded column, an in-screen tab row, a bordered rounded-bottom container holding
              a host header and a status bar around the screen's own content, and a 300px sidebar.

              Gated on IsTransfersScreen exactly as before — IsShowingPages means "the Avalonia page area,
              not the WebView", which is the occlusion question, and which mode the switcher is on is this
              wrapper's own separate question.

              The tab row's own click does not select a terminal tab — there is no per-tab SFTP session in
              this application, and building one is out of this wave's scope; see the notes' own open
              question and MainWindowViewModel.SelectFilesHostCommand for how this resolves it: a click
              reuses the same "Browse files" plumbing a pin click already does, honestly opening (or
              reusing) a second, SFTP-specific connection to that tab's host rather than pretending a session
              exists that does not.
            -->
            <Panel IsVisible="{Binding IsTransfersScreen}">
              <Grid RowDefinitions="Auto,*" Margin="26">
                <views:SessionTabRow Grid.Row="0" Classes="sftp"
                                      TabCommand="{Binding SelectFilesHostCommand}" />
                <Border Grid.Row="1" BorderBrush="{StaticResource Border}" BorderThickness="1"
                        CornerRadius="0,0,12,12" ClipToBounds="True">
                  <Grid ColumnDefinitions="*,Auto">
                    <Grid Grid.Column="0" RowDefinitions="Auto,*,Auto">
                      <views:SessionHeader Grid.Row="0"
                                            OpenLabel="Open terminal"
                                            OpenCommand="{Binding OpenTerminalForFilesHostCommand}"
                                            EmptyText="{Binding Transfers.Status}" />
                      <views:TransfersScreen Grid.Row="1" DataContext="{Binding Transfers}" />
                      <views:SessionStatusBar Grid.Row="2" />
                    </Grid>
                    <!--
                      Hides when no session is active — see ShowsQuickAccessSidebar — rather than always
                      drawn: the SFTP surface reaches this Panel before a host is chosen, and a sidebar with
                      an empty QUICK ACCESS and no host name to print would be furniture with nothing to say.
                      Collapsing frees the "Auto" column it sits in, so the pane column takes the width back.
                    -->
                    <views:SessionSidebar Grid.Column="1"
                                          IsVisible="{Binding ShowsQuickAccessSidebar, FallbackValue=False}" />
                  </Grid>
                </Border>
              </Grid>
            </Panel>

            <!-- ============ S3 ============ -->
            <!--
              The same screen as SFTP above, over the same view model, because an object store and an
              SFTP host are both an IRemoteFileStore and everything below the picker was written once.
              What differs is which picker is offered, and that is decided by the destination rather than
              by a toggle inside the screen — see ShowFiles, and the rail's own switcher segment that
              calls it now; see NavRail.axaml.

              Deliberately not given the full session shell above: a bucket is not a host, has no terminal tab
              to be the other end of a cross-surface button, and pins nothing the sidebar's QUICK ACCESS
              could show — so no tab row, no host header, no status bar and no sidebar. Wave B's own scope
              was the terminal and SFTP surfaces and left this plain; v5c-3 gives it the session shell's own
              LOOK without its machinery — a 26px padded column and the same bordered, radius-12 container —
              since design-notes/v5c-fidelity-notes.md calls for the fidelity pass here too, and there is
              nothing this screen does that needs the parts still withheld.
            -->
            <Panel IsVisible="{Binding IsBucketsScreen}">
              <Border Margin="26" BorderBrush="{StaticResource Border}" BorderThickness="1" CornerRadius="12"
                      ClipToBounds="True">
                <views:TransfersScreen DataContext="{Binding Transfers}" />
              </Border>
            </Panel>

            <!-- ============ KEYCHAIN ============ -->
            <!--
              Wrapped rather than bound directly, for the reason the vault column always was: this
              element's visibility is the shell's business and its data context is the vault, and put both
              on one element and IsVisible resolves against the vault as well, where IsKeychainScreen does
              not exist.
            -->
            <Panel IsVisible="{Binding IsKeychainScreen}">
              <views:KeychainScreen x:Name="VaultPane" DataContext="{Binding Vault}" />
            </Panel>

            <!-- ============ HOST KEYS ============ -->
            <!--
              Wrapped, like the two above and for the same reason: its data context is the screen's own
              view model, where IsKnownHostsScreen does not exist.
            -->
            <Panel IsVisible="{Binding IsKnownHostsScreen}">
              <views:KnownHostsScreen x:Name="PinsPane" DataContext="{Binding KnownHostsScreen}" />
            </Panel>

            <!-- ============ SNIPPETS ============ -->
            <!-- Wrapped, like the others whose data context is their own view model. -->
            <Panel IsVisible="{Binding IsSnippetsScreen}">
              <views:SnippetsScreen x:Name="SnippetsPane" DataContext="{Binding SnippetsScreen}" />
            </Panel>

            <!-- ============ LOGS ============ -->
            <!-- Wrapped, like the others whose data context is their own view model. -->
            <Panel IsVisible="{Binding IsLogsScreen}">
              <views:LogsScreen x:Name="LogsPane" DataContext="{Binding LogsScreen}" />
            </Panel>

            <!--
              v5c-2: the VAULTS panel that used to live here is gone. ShellScreen.Vaults now only ever shows
              through settings mode's own SettingsVaultsPage — see MainWindowViewModel.ShowScreen, which
              redirects it into EnterSettings before Screen is ever set — so IsVaultsScreen being true also
              means IsSettingsMode is true, and this whole page-area Panel (bound to !IsSettingsMode, above)
              is already hidden behind SettingsView by then. A Panel here would never have been drawn.

              v5c-3: IMPORT left this Panel the same way, and for the same reason. Import.dc.html draws the
              importer inside the settings chrome — SettingsNav lit on Preferences, a "Back to preferences"
              titlebar rather than the ordinary rail and titlebar this Panel belongs to — so ImportScreen now
              lives in SettingsView.axaml behind MainWindowViewModel.IsImportOpen, beside the seven settings
              pages rather than among the screens here. See ShowScreen's own translation of
              ShellScreen.Import for what still reaches it: the Preferences page's own "OPEN IMPORTER" row,
              and anything else that names the same destination.
            -->

          </Panel>

          <!-- ============ TERMINAL ============ -->
          <!--
            ◆ v5b's session shell for the terminal surface, built the same way SFTP's own wrapper above is —
            around the real terminal control rather than around a page.

            Gated on IsTerminalSurface rather than on IsShowingPages: the two are always exclusive, because
            Surface is a single ShellSurface value, so this Grid and the pages Panel above it are never both
            visible at once. That is what keeps the occlusion rule intact with the WebView now nested inside
            a padded column, a bordered container and a header row rather than sitting directly beside the
            page area the way it used to.

            THE RULE ITSELF DID NOT MOVE. NativeWebView's own IsVisible binding, below, is unchanged and is
            not hoisted to this Grid — hoisting it is the one alternative the pin-strip era of this file
            called out as unverified, and nesting the control deeper without touching its own binding is not
            that: IsTerminalShowing already depends on IsTerminalSurface, so this Grid's own visibility and
            the WebView's own visibility flip together on every surface change, driven by the same property
            change rather than one waiting on the other.

            The tab row's own click selects a terminal tab exactly as the old window-wide strip's did; see
            SelectTabCommand. "+" keeps its current meaning, quick connect, on both this row and SFTP's own.
          -->
          <Grid RowDefinitions="Auto,*" Margin="26" IsVisible="{Binding IsTerminalSurface}">
            <views:SessionTabRow Grid.Row="0" TabCommand="{Binding SelectTabCommand}" />
            <Border Grid.Row="1" BorderBrush="{StaticResource Border}" BorderThickness="1"
                    CornerRadius="0,0,12,12" ClipToBounds="True">
              <Grid ColumnDefinitions="*,Auto">
                <Grid Grid.Column="0" RowDefinitions="Auto,*,Auto">
                  <views:SessionHeader Grid.Row="0"
                                        OpenLabel="Open SFTP"
                                        OpenCommand="{Binding SelectFilesHostCommand}"
                                        OpenCommandParameter="{Binding SelectedTab}"
                                        EmptyText="no terminals open · press + or Ctrl+K, or choose a host and press Connect" />

                  <Panel Grid.Row="1" Background="{StaticResource Pane}">

                    <!--
                      The other thing that can be in the terminal's rectangle: a tab whose session does not
                      exist yet, or never will. Exclusive with the WebView below by construction — a
                      selected tab either has a session or it does not — which is what makes drawing it here
                      safe under the occlusion rule, the same way the page area is. See ConnectingCard.axaml.
                    -->
                    <views:ConnectingCard x:Name="ConnectingPane"
                                          IsVisible="{Binding IsConnectingShowing, FallbackValue=False}" />

                    <!--
                      One WebView hosting every terminal. Not one per tab: each WebView2 is a separate
                      browser process tree, so twenty tabs would cost twenty of them.

                      FallbackValue, because a compiled binding with no DataContext yields UnsetValue,
                      IsVisible then falls back to its default of true, and the occlusion comes back
                      silently. Not reachable at runtime — the DataContext is set before the window is shown
                      — but it is what the previewer does.
                    -->
                    <NativeWebView x:Name="Terminal"
                                   IsVisible="{Binding IsTerminalShowing, FallbackValue=False}" />

                  </Panel>

                  <views:SessionStatusBar Grid.Row="2" ShowsEncoding="True" />
                </Grid>
                <!--
                  Hides when no session is active — see ShowsQuickAccessSidebar — rather than always drawn:
                  the terminal surface is reachable with no tab open at all (the rail's own SSH segment), and
                  a sidebar naming no host would be furniture with nothing to say. Collapsing frees the
                  "Auto" column it sits in, so the pane column takes the width back.
                -->
                <views:SessionSidebar Grid.Column="1" ShowsSnips="True"
                                      IsVisible="{Binding ShowsQuickAccessSidebar, FallbackValue=False}" />
              </Grid>
            </Border>
          </Grid>

        </Panel>
      </Grid>

      <!--
        v5c: the settings mode, which replaces the titlebar above and everything in this Panel below it with
        its own — see SettingsView.axaml and TitleBar's own IsVisible next to it in this file. Later in the
        Panel than the unlocked Grid, on the same reasoning the setup Border below gives for its own
        position: Avalonia z-order draws a later sibling over an earlier one, and SettingsView's own
        Background is opaque, so it covers the rail and the page area completely rather than needing them
        hidden out from under it. The unlocked Grid keeps IsEnabled bound to the negation above so a Tab
        press cannot walk keyboard focus into a control this mode has covered.

        Earlier than the palette and the host-key decision, both further down this same Panel, so either can
        still interrupt settings mode exactly as it already interrupts every other screen — a host key that
        changes while the sync loop is running does not stop being worth answering because the window
        happens to be showing Settings.
      -->
      <views:SettingsView IsVisible="{Binding IsSettingsMode}" />

      <!--
        Setup and unlock. Later in the Panel, so it is above the application content in Avalonia's z-order —
        which covers Avalonia-drawn content and nothing else. The terminal is collapsed rather than covered;
        see the occlusion rule above.
      -->
      <Border Background="{StaticResource Canvas}" IsVisible="{Binding !IsUnlocked}">
        <Panel>

          <Border Classes="card" IsVisible="{Binding IsStarting}">
            <StackPanel Spacing="10">
              <TextBlock Classes="heading" Text="DodoSSH" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <Border Classes="card" IsVisible="{Binding IsNeedingServer}">
            <StackPanel Spacing="12">
              <TextBlock Classes="heading" Text="Connect to your server" />
              <TextBlock Classes="hint"
                         Text="One address is all this needs. The identity provider, the client id and the scopes all come from the server itself." />
              <!--
                Named because signing out lands here with the keyboard needing somewhere to go: the vault's
                controls have just been collapsed, and Focus() on a collapsed control is a no-op that is
                not replayed. See MainWindow.axaml.cs.
              -->
              <TextBox x:Name="ServerUrlBox" Text="{Binding ServerUrl}"
                       PlaceholderText="https://dodossh.example" />
              <Button Classes="accent" Content="SIGN IN WITH YOUR BROWSER" Command="{Binding SignInCommand}"
                      IsEnabled="{Binding !IsBusy}" HorizontalAlignment="Left" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <Border Classes="card" IsVisible="{Binding IsNeedingEnrollment}">
            <StackPanel Spacing="12">
              <TextBlock Classes="heading" Text="Choose a keychain passphrase" />
              <TextBlock Classes="hint"
                         Text="This passphrase never leaves this machine, and the server cannot reset it. It is the only thing standing between a stolen copy of the database and every credential in your keychain." />
              <TextBox Text="{Binding Passphrase}" PlaceholderText="passphrase" PasswordChar="•" />
              <TextBox Text="{Binding ConfirmPassphrase}" PlaceholderText="again" PasswordChar="•" />
              <Button Classes="accent" Content="CREATE MY KEYCHAIN" Command="{Binding EnrollCommand}"
                      IsEnabled="{Binding !IsBusy}" HorizontalAlignment="Left" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <!--
            Shown once and impossible to skip. This is the only moment the code exists, and losing it
            together with the passphrase means the keychain is unrecoverable — there is no server-side reset
            by design.
          -->
          <Border Classes="card" IsVisible="{Binding IsShowingRecoveryCode}">
            <StackPanel Spacing="12">
              <TextBlock Classes="heading" Text="Write this recovery code down" />
              <TextBlock Classes="hint"
                         Text="It is shown once and is not stored anywhere. Without it, forgetting your passphrase means losing the keychain: nobody — including whoever runs the server — can recover it for you." />
              <Border Background="{StaticResource Raised}" BorderBrush="{StaticResource Border}"
                      BorderThickness="1" CornerRadius="6" Padding="14">
                <SelectableTextBlock Classes="mono" Text="{Binding RecoveryCode}"
                                     FontSize="17" Foreground="{StaticResource Accent}"
                                     TextWrapping="Wrap" />
              </Border>
              <!--
                ◆ COPY, beside the tick rather than inside the box. The code is selectable and has been
                since this card existed, on the argument that refusing to let somebody take it only pushes
                them to a screenshot — this is that argument finished, because "select 64 characters of
                letter-spaced monospace without missing one" is a thing people get wrong silently.

                It is the one secret this application offers to the clipboard on purpose; see
                MainWindowViewModel.CopyRecoveryCode for why that is right here and refused for a private
                key, and for what the status line below says afterwards.
              -->
              <StackPanel Orientation="Horizontal" Spacing="10">
                <Button Classes="ghost" Content="COPY CODE"
                        Command="{Binding CopyRecoveryCodeCommand}"
                        ToolTip.Tip="Puts the code on the clipboard. Paste it into your password manager now — it is shown once and the next thing you copy replaces it." />
                <CheckBox IsChecked="{Binding RecoveryCodeWrittenDown}"
                          Content="I have written it down somewhere safe"
                          VerticalAlignment="Center" />
              </StackPanel>
              <Button Classes="accent" Content="CONTINUE" Command="{Binding ConfirmRecoveryCodeCommand}"
                      HorizontalAlignment="Left" />
              <TextBlock Classes="hint" Text="{Binding StatusMessage}" />
            </StackPanel>
          </Border>

          <!--
            The unlock card, and the sign-out confirmation that replaces it. Both live in their own files:
            nothing inside this window can be laid out by a test — WebView2's adapter refuses the headless
            session's thread — so markup that stays here is markup nobody can measure. See UnlockCard.axaml.
          -->
          <Border Classes="card" IsVisible="{Binding IsAskingForThePassphrase}">
            <views:UnlockCard x:Name="UnlockPane" />
          </Border>

          <!--
            The confirmation, in place of the unlock card rather than under it: the card is already close
            to the height the window guarantees at its minimum, and a screen a user cannot read all of is
            worse than one that shows one question at a time.
          -->
          <Border Classes="card" IsVisible="{Binding IsConfirmingSignOut}">
            <views:SignOutCard />
          </Border>

        </Panel>
      </Border>

      <!--
        Last, so it is over everything Avalonia draws. It is over the terminal too, because opening it
        collapses the terminal — see IsTerminalShowing.
      -->
      <views:QuickConnect x:Name="Palette" IsVisible="{Binding IsSearching}" />

      <!--
        ◆ Later still, and the order between these two is a ranking rather than an accident: the palette is
        something the user opened and can close, and a host-key decision is a connection waiting on them. It
        used to be two banners at the top of the hosts screen, which is why the shell navigated there before
        the question could be asked — see HostKeyCard.axaml, and MainWindowViewModel.OnVaultConnectionFailed
        for what that cost every other screen.

        Wrapped, like every other child here whose data context is the vault: the visibility is the shell's
        business — it is the flag that also collapses the WebView — and IsHostKeyDecisionShowing does not
        exist on a VaultViewModel, which with compiled bindings is a build error rather than a card that
        silently never appears.
      -->
      <Panel IsVisible="{Binding IsHostKeyDecisionShowing, FallbackValue=False}">
        <views:HostKeyCard x:Name="HostKeyPane" DataContext="{Binding Vault}" />
      </Panel>

    </Panel>

    <!--
      A fourth row, and a row rather than an overlay for the reason the occlusion rule above gives: this
      appears while a terminal may be open, and anything drawn in the WebView's rectangle is sliced. Taking
      height from the row above moves the native control's bounds instead of covering it, which is the one
      arrangement that works — the same one TitleBar and StatusBar already rely on.

      It is a separate control because nothing in this file can be measured by a test, and a strip with two
      buttons and a version string of unknown length is exactly the shape that arranges one of them off the
      edge. See UpdateBanner.axaml.

      ◆ THE DATA CONTEXT IS SET HERE, and the banner did nothing at all until it was.

      Unlike the titlebar and the status bar, which are typed to this window's own view model and inherit its
      context, the banner is typed to UpdateViewModel — it is one screen's control and its layout suite hosts
      it over that view model alone. Inheriting the shell's context instead left every compiled binding
      inside it resolving against the wrong type and failing silently: no headline, and both Commands null,
      so the strip appeared, hovered and pressed like a real banner and neither button did anything.

      IsVisible is unqualified because the context is set on this same element, which resolves it against
      UpdateViewModel too — the rule the page area's wrappers above are wrapped for. It needs no wrapper: the
      flag it binds is the banner's own, unlike IsHostsScreen and its siblings, which belong to the shell.

      FallbackValue, for the reason the WebView and the connecting card carry one: a compiled binding with
      no DataContext yields UnsetValue, IsVisible falls back to true, and the previewer would show a banner
      announcing an update that does not exist.
    -->
    <!--
      v5c: wrapped rather than given a third condition of its own — UpdateBanner sets its own DataContext to
      UpdateViewModel on this same element, so a binding against IsSettingsMode has to live on an ancestor
      whose context is still the shell. The design's settings titlebar has no room for this strip; hiding it
      here rather than teaching the banner about a mode it otherwise has no reason to know about.
    -->
    <Panel Grid.Row="2" IsVisible="{Binding !IsSettingsMode}">
      <views:UpdateBanner DataContext="{Binding Updates}"
                          IsVisible="{Binding IsBannerShowing, FallbackValue=False}" />
    </Panel>

    <views:StatusBar Grid.Row="3" IsVisible="{Binding !IsSettingsMode}" />

  </Grid>

</Window>
