<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
             x:Class="DodoSSH.Client.App.Views.TransfersScreen"
             x:DataType="vm:TransfersViewModel">

  <!--
    File transfer: this machine on the left, the host on the right, and the queue underneath.

    Two things about this screen are worth knowing before reading the markup. It has its own CONNECT
    button, because SSH.NET cannot open an SFTP subsystem on a transport that is already carrying a shell —
    so browsing a host's files is a second authenticated connection rather than a second channel, and
    pretending otherwise would hide a second login from the person whose audit log it appears in. And the
    panes are symmetrical apart from one column: PERMS is remote-only, because a POSIX mode is not a fact
    about a file on the machine this client is developed on.

    ── v3 ────────────────────────────────────────────────────────────────────────────────────────────────
    ◆ THE CONNECT BAR IS GONE, and everything it held is inside the right-hand pane now.

    It was a 44-pixel strip across the top of the screen holding a heading, a picker, a password box and a
    button — chrome spanning both panes to configure one of them, and drawn at full width whether or not
    anything was ever going to be connected. Underneath it sat a pane that was empty for exactly the same
    reason, saying so in a sentence nobody had to be told twice.

    So the pane says it instead. Disconnected, the right-hand half is an invitation where the listing would
    be: what this screen is for, what pressing the button will cost, and the button. Connected, the
    invitation is replaced by the listing and the connection states itself in a strip above it, beside the
    control that closes it — which is the arrangement the two facts were always about. The heading the bar
    carried is not reprinted anywhere: the tab in the strip says SFTP or S3, and it says it whether or not
    this screen is showing.

    What the design has and this does not: `sftp over bastion-eu`, which needs jump hosts the connection
    layer has not got. See docs/design-import-gaps.md.
  -->

  <UserControl.Styles>
    <!--
      ── v5b ─────────────────────────────────────────────────────────────────────────────────────────────
      A directory used to be marked by colour alone — this application shipped no icon set when that rule
      was written. It ships one now: v5b embeds Material Icons as IconFont, already spent on the nav rail,
      the drawer and the session shell's own sidebar, and per SFTP.dc.html a file row draws a 14px glyph of
      its own — folder for a directory, and the closest classic Material Icons has to the design's own
      Material-Symbols-only "draft": insert_drive_file, a plain document rather than a page with a folded
      corner. Recorded as a deviation rather than silently swapped, because "draft" is simply not a glyph
      this font contains.

      So NAME's colour stopped being the directory's own mark — the glyph is now — and become what the
      design says instead: a folder is white and medium weight, a file is TextDim and regular. Blue is not
      spent here any more; Info keeps meaning "a distinct scope" everywhere else it already did, and this
      screen no longer borrows it.

      A second colour still comes from the mode, and it still overrides the first rather than competing with
      it: an executable is Live green in NAME — "live, yours, something that runs" — regardless of whether
      the row beside it is asking for the folder colours or the file ones. PERMS keeps its own, separate
      amber for a mode anyone may write to, on the same reasoning as before: NAME says what a row is, PERMS
      says what is notable about how it is set, and the two never compete for one TextBlock.
    -->
    <Style Selector="TextBlock.entry">
      <Setter Property="Foreground" Value="{StaticResource TextDim}" />
    </Style>
    <Style Selector="TextBlock.entry.dir">
      <Setter Property="Foreground" Value="{StaticResource Text}" />
      <Setter Property="FontWeight" Value="Medium" />
    </Style>
    <!--
      Declared after .dir on purpose — Avalonia has no specificity, and an executable that is also somehow
      the target row would need this rule to win. It never is in practice; see SftpEntry, which reads the
      execute bit off a file's own mode and nothing else's.
    -->
    <Style Selector="TextBlock.entry.exec">
      <Setter Property="Foreground" Value="{StaticResource Live}" />
    </Style>

    <!--
      The glyph beside NAME. One quiet colour regardless of folder or file — the design's own renderVals
      gives both the same default iconFg — because the glyph itself already says which one a row is; the
      colour underneath it is furniture, on the same TextFaint-family step TextBlock.fieldglyph already uses
      for a mark meant to be skipped rather than read.
    -->
    <Style Selector="TextBlock.entryicon">
      <Setter Property="FontFamily" Value="{StaticResource IconFont}" />
      <Setter Property="FontSize" Value="14" />
      <Setter Property="Foreground" Value="{StaticResource TextGhost}" />
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="HorizontalAlignment" Value="Center" />
    </Style>

    <!--
      SIZE and MODIFIED, both quieter than the design's own literal values ask for — rgb(93,95,116) and
      rgb(110,112,137) sit a step below every text colour this palette already names. Per
      design-notes/v5b-fidelity-notes.md's own instruction for values this close, the nearest existing steps
      are reused rather than two new keys added for a difference nobody would see: TextGhost for SIZE, which
      the design draws a shade darker than MODIFIED, and TextFaint — one step lighter — for MODIFIED, which
      keeps the two in the same order the design puts them in even though neither is the design's exact hex.
    -->
    <Style Selector="TextBlock.entrysize">
      <Setter Property="Foreground" Value="{StaticResource TextGhost}" />
    </Style>
    <Style Selector="TextBlock.entrydate">
      <Setter Property="Foreground" Value="{StaticResource TextFaint}" />
    </Style>

    <!--
      Faint by default, as this column has always been: a mode is there so its absence would be noticed. It
      steps up to amber only when it has something to say, which is the whole reason the default is quiet.
    -->
    <Style Selector="TextBlock.perms">
      <Setter Property="Foreground" Value="{StaticResource TextFaint}" />
    </Style>
    <!--
      Warn rather than WarnText, which is the muted amber a warning card writes its sentences in. At 9.5px
      against TextFaint that one is a shade, not a signal, and a marker nobody notices is the same as no
      marker at all.
    -->
    <Style Selector="TextBlock.perms.loose">
      <Setter Property="Foreground" Value="{StaticResource Warn}" />
    </Style>

    <!--
      ── v5b: the TRANSFERS strip's own progress track ────────────────────────────────────────────────────
      5px, 3px radius, Chip's own fill under an AccentGradient indicator — the design's own slim bar. A real
      0% is what a queued transfer draws with this: the indicator's width is nothing, which reads exactly as
      the design's own queued sample row does (a bar the same colour as its own track). No indeterminate
      animation is drawn for it — a queued transfer really is at zero, so zero is the honest thing to show,
      not a state this screen has to invent furniture for.
    -->
    <Style Selector="ProgressBar.transferbar">
      <Setter Property="Height" Value="5" />
      <Setter Property="MinHeight" Value="5" />
      <Setter Property="CornerRadius" Value="3" />
      <Setter Property="Background" Value="{StaticResource Chip}" />
      <Setter Property="Foreground" Value="{StaticResource AccentGradient}" />
    </Style>

    <!--
      The strip's own right-aligned status word. Quiet by default — a stopped transfer's "stopped" gets no
      rule of its own and falls through to this — and three states get their own colour: in motion, kept, and
      refused. See TransferRowViewModel.StatusWord for what word each state actually prints.
    -->
    <Style Selector="TextBlock.transferstatus">
      <Setter Property="Foreground" Value="{StaticResource TextFaint}" />
    </Style>
    <Style Selector="TextBlock.transferstatus.running">
      <Setter Property="Foreground" Value="{StaticResource TextDim}" />
    </Style>
    <Style Selector="TextBlock.transferstatus.done">
      <Setter Property="Foreground" Value="{StaticResource Live}" />
    </Style>
    <Style Selector="TextBlock.transferstatus.failed">
      <Setter Property="Foreground" Value="{StaticResource Danger}" />
    </Style>
  </UserControl.Styles>

  <Grid RowDefinitions="*,Auto">

    <!-- ============ The two panes ============ -->
    <Grid Grid.Row="0" ColumnDefinitions="*,64,*">

      <!-- ==== This machine ==== -->
      <!--
        AllowDrop on the pane rather than on the list, because an empty directory lays its ListBox out at
        zero height behind the empty-state sentence — a handler on the list would have nothing to hit.
        This side takes remote rows only; see TransfersScreen.axaml.cs.
      -->
      <Grid Grid.Column="0" x:Name="LocalPane" RowDefinitions="Auto,Auto,Auto,*" DragDrop.AllowDrop="True">

        <!--
          ── v5b ─────────────────────────────────────────────────────────────────────────────────────────
          Per SFTP.dc.html: a 10px tracked-out label beside the current path in mono 13.5, and the pane's
          affordances restyled to quiet glyphs rather than dropped. "THIS MACHINE" becomes the design's own
          "LOCAL" — nothing in this file's own comments ever argued for that particular wording over the
          design's, unlike HOST/BUCKET across the arrow column, which keeps its own words for a reason
          recorded where it is drawn.

          UP is arrow_upward here and arrow_downward on the host/bucket pane below, matching the design's own
          two glyphs exactly rather than picking one "up a directory" icon for both — both buttons do the same
          thing (go to the parent directory), and the pair reading differently is the design's own choice
          faithfully carried over rather than a functional difference invented to justify it.
        -->
        <!--
          ◆ THE PATH SITS IN THE STAR COLUMN, ALONE, AND THAT PLACEMENT IS LOAD-BEARING.

          TextTrimming only acts when measure hands the block a finite width, and a horizontal StackPanel
          never does — it measures every child at infinity, takes the full answer, and an Auto grid column
          passes that on. With the path in a StackPanel beside the label, a directory deep enough — the
          remote pane meets one on any real host, this pane on any machine whose profile path is long —
          made the header wider than the pane and pushed the icon buttons past the window's own edge. The
          layout suite caught it at the 472-pixel session budget, on the one machine whose home directory
          was long enough to arm it. Star column: bounded width, working ellipsis, buttons that stay.
        -->
        <Border Grid.Row="0" Padding="0,0,0,10" BorderThickness="0">
          <Grid ColumnDefinitions="Auto,*,Auto">
            <TextBlock Grid.Column="0" Classes="label" FontSize="10" Text="LOCAL"
                       VerticalAlignment="Center" Margin="0,0,12,0" />
            <TextBlock Grid.Column="1" Classes="mono" FontSize="13.5" FontWeight="Medium"
                       Foreground="{StaticResource TextDim}" VerticalAlignment="Center"
                       HorizontalAlignment="Left"
                       Text="{Binding LocalPath}" TextTrimming="CharacterEllipsis"
                       ToolTip.Tip="{Binding LocalPath}" />
            <StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="12,0,0,0">
              <!--
                The drives, because the breadcrumb cannot reach them: above C:\ is a list rather than a
                directory. Without this the pane is stuck on whichever drive the user profile is on. Chips
                rather than the pane's own quiet icon buttons, because a drive is named rather than a single
                glyph — "C:" has no honest Material Icons equivalent.
              -->
              <ItemsControl ItemsSource="{Binding LocalRoots}" VerticalAlignment="Center">
                <ItemsControl.ItemsPanel>
                  <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" Spacing="4" />
                  </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                  <DataTemplate x:DataType="vm:CrumbViewModel">
                    <Button Classes="panechip" Content="{Binding Name}"
                            Command="{Binding $parent[ItemsControl].((vm:TransfersViewModel)DataContext).GoLocalCommand}"
                            CommandParameter="{Binding Path}" />
                  </DataTemplate>
                </ItemsControl.ItemTemplate>
              </ItemsControl>
              <Button Classes="paneicon" Command="{Binding LocalUpCommand}"
                      ToolTip.Tip="Up one directory">
                <TextBlock FontFamily="{StaticResource IconFont}" FontSize="16" Text="&#xE5D8;" />
              </Button>
              <Button Classes="paneicon" Command="{Binding RefreshLocalCommand}"
                      ToolTip.Tip="Refresh">
                <TextBlock FontFamily="{StaticResource IconFont}" FontSize="16" Text="&#xE5D5;" />
              </Button>
            </StackPanel>
          </Grid>
        </Border>

        <ItemsControl Grid.Row="1" ItemsSource="{Binding LocalTrail}" Margin="12,6,12,4">
          <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
              <WrapPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
          </ItemsControl.ItemsPanel>
          <ItemsControl.ItemTemplate>
            <DataTemplate x:DataType="vm:CrumbViewModel">
              <StackPanel Orientation="Horizontal">
                <Button Classes="flat" Padding="3,1"
                        Command="{Binding $parent[ItemsControl].((vm:TransfersViewModel)DataContext).GoLocalCommand}"
                        CommandParameter="{Binding Path}">
                  <TextBlock Classes="mono" Text="{Binding Name}" FontSize="11"
                             Foreground="{StaticResource TextDim}" />
                </Button>
                <TextBlock Classes="mono" Text="›" FontSize="11"
                           Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
              </StackPanel>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>

        <!--
          v5b: 20 for the glyph column rather than the design's bare icon with no gutter, because a Material
          glyph centred with no reserved width drifts as the font's own advance width varies between folder
          and insert_drive_file. 60/56 for SIZE/MODIFIED are narrower than the design's literal 80/78 — see
          the row template below for why the whole column set was chosen against 204 pixels rather than the
          design's own 1920-pixel canvas.
        -->
        <Grid Grid.Row="2" ColumnDefinitions="20,*,60,56" Margin="0,2,10,4">
          <TextBlock Grid.Column="1" Classes="label" Text="NAME" FontSize="9.5" Margin="8,0,8,0" />
          <TextBlock Grid.Column="2" Classes="label" Text="SIZE" FontSize="9.5" HorizontalAlignment="Right" />
          <TextBlock Grid.Column="3" Classes="label" Text="MODIFIED" FontSize="9.5" HorizontalAlignment="Right" />
        </Grid>

        <!--
          h38, radius 7, Track for hover and selected — see ListBox.filerows in App.axaml — and no rowmark:
          the design has nothing to distinguish "selected" from "hovered" beyond which one is currently true,
          and :selected alone already carries that once the pointer moves on.
        -->
        <ListBox Grid.Row="3" x:Name="LocalList" Classes="filerows" ItemsSource="{Binding LocalEntries}"
                 SelectedItem="{Binding SelectedLocalEntry}">
          <ListBox.ItemTemplate>
            <DataTemplate x:DataType="vm:LocalEntryRowViewModel">
              <Grid ColumnDefinitions="20,*,60,56" Height="38" Margin="14,0,10,0">
                <TextBlock Grid.Column="0" Classes="entryicon" Text="{Binding IconGlyph}" />
                <TextBlock Grid.Column="1" Classes="mono entry" Classes.dir="{Binding IsNavigable}"
                           Text="{Binding Name}" FontSize="13.5"
                           Margin="10,0,8,0" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
                <TextBlock Grid.Column="2" Classes="mono entrysize" Text="{Binding Size}" FontSize="12"
                           HorizontalAlignment="Right" VerticalAlignment="Center" />
                <TextBlock Grid.Column="3" Classes="mono entrydate" Text="{Binding Modified}" FontSize="12"
                           Margin="8,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Center" />
              </Grid>
            </DataTemplate>
          </ListBox.ItemTemplate>
        </ListBox>

        <TextBlock Grid.Row="3" Classes="hint" FontSize="12" Margin="24" MaxWidth="260"
                   HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
                   Text="Nothing in this folder. Use the trail above to go somewhere else."
                   IsVisible="{Binding !HasLocalEntries}" />

        <!--
          The drop highlight, over the whole pane and last so it is on top.

          IsHitTestVisible="False" is not optional. An overlay that takes part in hit testing swallows the
          DragOver events underneath it the moment it appears — so the pointer leaves, the highlight never
          clears, and the drop lands nowhere.
        -->
        <Border Grid.Row="0" Grid.RowSpan="4" IsHitTestVisible="False"
                Background="{StaticResource AccentWash}" BorderBrush="{StaticResource Accent}"
                BorderThickness="2" IsVisible="{Binding IsLocalDropTarget}" />

      </Grid>

      <!-- ==== The two directions ==== -->
      <Border Grid.Column="1" Background="{StaticResource Sidebar}"
              BorderBrush="{StaticResource Border}" BorderThickness="1,0">
        <StackPanel VerticalAlignment="Center" Spacing="10" Margin="6">
          <!--
            Pointing at the pane the file is going to, which is the only reading that survives the panes
            being side by side: the left-hand pane is this machine, so an upload points right.
          -->
          <Button Classes="ghost" Content="→" HorizontalAlignment="Stretch"
                  HorizontalContentAlignment="Center"
                  Command="{Binding UploadCommand}" IsEnabled="{Binding CanUpload}"
                  ToolTip.Tip="Upload the selected file to the directory showing on the host" />
          <Button Classes="ghost" Content="←" HorizontalAlignment="Stretch"
                  HorizontalContentAlignment="Center"
                  Command="{Binding DownloadCommand}" IsEnabled="{Binding CanDownload}"
                  ToolTip.Tip="Download the selected file into the folder showing on this machine" />
        </StackPanel>
      </Border>

      <!-- ==== The host ==== -->
      <!--
        Six rows rather than five: the connection's own strip is the new one, and it is inside this pane
        rather than above both because it is about this pane and nothing else.
      -->
      <Grid Grid.Column="2" x:Name="RemotePane" RowDefinitions="Auto,Auto,Auto,Auto,Auto,*"
            DragDrop.AllowDrop="True">

        <!--
          ── v5b ─────────────────────────────────────────────────────────────────────────────────────────
          HOST/BUCKET keep their own words rather than adopting the design's generic "REMOTE": the comment
          this replaced already carried a documented honesty reason — the pair is the difference between a
          directory tree and a flat namespace with inferred folders — and that reason still holds, so only
          the style moves to the design's 10px tracked label, not the wording.

          UP is arrow_downward here, matching arrow_upward on the local pane above per the design's own two
          distinct glyphs; see that pane's own remark on why the pair differs without the actions differing.
        -->
        <!--
          The path is alone in the star column for the reason the local pane's header remark spells out:
          TextTrimming needs the finite width only a star column gives it, and this is the pane where the
          long path is not even unusual — it is any host with a deep directory tree.
        -->
        <Border Grid.Row="0" Padding="0,0,0,10" BorderThickness="0">
          <Grid ColumnDefinitions="Auto,*,Auto">
            <StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="12" VerticalAlignment="Center"
                        Margin="0,0,12,0">
              <TextBlock Classes="label" FontSize="10" Text="HOST" VerticalAlignment="Center"
                         IsVisible="{Binding ShowsHostPicker}" />
              <TextBlock Classes="label" FontSize="10" Text="BUCKET" VerticalAlignment="Center"
                         IsVisible="{Binding ShowsBucketPicker}" />
            </StackPanel>
            <TextBlock Grid.Column="1" Classes="mono" FontSize="13.5" FontWeight="Medium"
                       Foreground="{StaticResource TextDim}" VerticalAlignment="Center"
                       HorizontalAlignment="Left"
                       Text="{Binding RemotePath}" TextTrimming="CharacterEllipsis"
                       ToolTip.Tip="{Binding RemotePath}" />
            <StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="12,0,0,0">
              <Button Classes="paneicon" Command="{Binding RemoteUpCommand}"
                      IsEnabled="{Binding IsConnected}" ToolTip.Tip="Up one directory">
                <TextBlock FontFamily="{StaticResource IconFont}" FontSize="16" Text="&#xE5DB;" />
              </Button>
              <Button Classes="paneicon" Command="{Binding RefreshRemoteCommand}"
                      IsEnabled="{Binding IsConnected}" ToolTip.Tip="Refresh">
                <TextBlock FontFamily="{StaticResource IconFont}" FontSize="16" Text="&#xE5D5;" />
              </Button>
              <Button Classes="paneicon danger" Command="{Binding DeleteRemoteCommand}"
                      IsEnabled="{Binding CanDeleteRemote}" ToolTip.Tip="Delete on the host">
                <TextBlock FontFamily="{StaticResource IconFont}" FontSize="15" Text="&#xE872;" />
              </Button>
            </StackPanel>
          </Grid>
        </Border>

        <!--
          ◆ WHAT IS OPEN, AND WHAT CLOSES IT. Only while something is.

          v5b drops the account-at-host chip this row used to carry beside DISCONNECT: SessionHeader now
          prints the very same address above this whole screen — see MainWindowViewModel.SessionAddress,
          which already reads Transfers.ConnectedTo on the SFTP surface — and repeating it here stopped being
          information and started being the thing squeezing DISCONNECT off the edge. At the session shell's
          own narrower budget this pane is 204 pixels wide once QUICK ACCESS is showing beside it, where the
          170-pixel chip this row used to carry would have taken most of that on its own.

          The status line still is not here, for the reason the comment this replaced gave: there is no room
          for a sentence beside anything else this row holds. It is at the foot of the screen instead — see
          the queue's own strip.
        -->
        <Border Grid.Row="1" Padding="12,6" Background="{StaticResource Raised}"
                BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,0,0,1"
                IsVisible="{Binding IsConnected}">
          <Button Classes="headerghost" HorizontalAlignment="Right" Content="DISCONNECT"
                  Command="{Binding DisconnectCommand}" />
        </Border>

        <!--
          The question DELETE asks. Under the button rather than over the pane, so the row it is about is
          still on screen and still selected while it is being answered — and it names the full path rather
          than the file, because a name is the half that does not identify anything.

          This is the strongest warning on any of these screens, and deliberately: everything else this
          application deletes is a tombstone against a copy the server still has, and a file on somebody's
          host is bytes with nothing behind them.

          A WrapPanel for the two buttons rather than a horizontal StackPanel — v5b's own change, made once
          the session shell's sidebar could take this pane down to 204 pixels: "DELETE ON THE HOST" and
          CANCEL side by side want closer to 220, so at the narrow width they wrap onto their own line
          instead of one of them going off the edge, which the WrapPanel achieves for free.
        -->
        <Border Grid.Row="2" Padding="12,10" Background="{StaticResource DangerWash}"
                BorderBrush="{StaticResource DangerSoft}" BorderThickness="0,0,0,1"
                IsVisible="{Binding IsConfirmingRemoteDeletion}">
          <StackPanel Spacing="7">
            <TextBlock Classes="heading" FontSize="14" TextWrapping="Wrap"
                       Text="{Binding PendingRemoteDeletion.Question}" />
            <SelectableTextBlock Classes="mono" FontSize="11.5" TextWrapping="Wrap"
                                 Foreground="{StaticResource Danger}"
                                 Text="{Binding PendingRemoteDeletion.FullPath}" />
            <TextBlock Foreground="{StaticResource WarnText}" FontSize="12" TextWrapping="Wrap"
                       Text="{Binding PendingRemoteDeletion.Consequence}" />
            <WrapPanel>
              <Button Classes="danger" Content="DELETE ON THE HOST" Margin="0,0,8,4"
                      Command="{Binding ConfirmDeleteRemoteCommand}" IsEnabled="{Binding !IsBusy}" />
              <Button Classes="ghost" Content="CANCEL" Margin="0,0,0,4"
                      Command="{Binding CancelDeleteRemoteCommand}" />
            </WrapPanel>
          </StackPanel>
        </Border>

        <Grid Grid.Row="3" ColumnDefinitions="*,Auto" Margin="12,6,12,4">
          <ItemsControl Grid.Column="0" ItemsSource="{Binding RemoteTrail}" VerticalAlignment="Center">
            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal" />
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
              <DataTemplate x:DataType="vm:CrumbViewModel">
                <StackPanel Orientation="Horizontal">
                  <TextBlock Classes="mono" Text="/" FontSize="11"
                             Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
                  <Button Classes="flat" Padding="3,1"
                          Command="{Binding $parent[ItemsControl].((vm:TransfersViewModel)DataContext).GoRemoteCommand}"
                          CommandParameter="{Binding Path}">
                    <TextBlock Classes="mono" Text="{Binding Name}" FontSize="11"
                               Foreground="{StaticResource TextDim}" />
                  </Button>
                </StackPanel>
              </DataTemplate>
            </ItemsControl.ItemTemplate>
          </ItemsControl>

          <!--
            Making a directory sits here, beside the path it would be made in, rather than with the queue's
            controls. It exists because the queue refuses to overwrite: without somewhere else to put a file,
            "that name is already taken" is a dead end.

            v5b narrows the box from 140 to 90: at the session shell's own 204-pixel width once QUICK ACCESS
            is showing, 140 plus MKDIR plus the spacing between them ran past the pane's own edge. Still wide
            enough for a real directory name to be typed and read back, which is what this box is for; a
            name that outgrows it scrolls inside the box the way every TextBox already does.
          -->
          <StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6"
                      IsVisible="{Binding IsConnected}">
            <TextBox Width="90" Text="{Binding NewRemoteFolder}" PlaceholderText="new directory" />
            <Button Classes="ghost" Content="MKDIR" Command="{Binding CreateRemoteFolderCommand}" />
          </StackPanel>
        </Grid>

        <!--
          v5b: PERMS keeps its own column — a real fact about a file on the host, dropped nowhere — but at
          48 pixels rather than the drawer's own wider habit, and SIZE/MODIFIED shrink to match the local
          pane's narrower numbers. See the local pane's own remark on why 204 rather than the design's 1920.
        -->
        <Grid Grid.Row="4" ColumnDefinitions="20,*,56,52,48" Margin="0,2,10,4">
          <TextBlock Grid.Column="1" Classes="label" Text="NAME" FontSize="9.5" Margin="8,0,8,0" />
          <TextBlock Grid.Column="2" Classes="label" Text="SIZE" FontSize="9.5" HorizontalAlignment="Right" />
          <TextBlock Grid.Column="3" Classes="label" Text="MODIFIED" FontSize="9.5" HorizontalAlignment="Right" />
          <TextBlock Grid.Column="4" Classes="label" Text="PERMS" FontSize="9.5" HorizontalAlignment="Right" />
        </Grid>

        <ListBox Grid.Row="5" x:Name="RemoteList" Classes="filerows" ItemsSource="{Binding RemoteEntries}"
                 SelectedItem="{Binding SelectedRemoteEntry}">
          <ListBox.ItemTemplate>
            <DataTemplate x:DataType="vm:RemoteEntryRowViewModel">
              <Grid ColumnDefinitions="20,*,56,52,48" Height="38" Margin="14,0,10,0">
                <TextBlock Grid.Column="0" Classes="entryicon" Text="{Binding IconGlyph}" />
                <TextBlock Grid.Column="1" Classes="mono entry" Classes.dir="{Binding IsNavigable}"
                           Classes.exec="{Binding IsExecutable}"
                           Text="{Binding Name}" FontSize="13.5"
                           Margin="10,0,8,0" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
                <TextBlock Grid.Column="2" Classes="mono entrysize" Text="{Binding Size}" FontSize="12"
                           HorizontalAlignment="Right" VerticalAlignment="Center" />
                <TextBlock Grid.Column="3" Classes="mono entrydate" Text="{Binding Modified}" FontSize="12"
                           Margin="8,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Center" />
                <TextBlock Grid.Column="4" Classes="mono perms" Classes.loose="{Binding IsWorldWritable}"
                           Text="{Binding Permissions}" FontSize="10" Margin="6,0,0,0"
                           HorizontalAlignment="Right" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
              </Grid>
            </DataTemplate>
          </ListBox.ItemTemplate>
        </ListBox>

        <!--
          A directory with nothing in it. It no longer has to say anything about not being connected: that
          state is the invitation below, which covers this pane whole.
        -->
        <TextBlock Grid.Row="5" Classes="hint" FontSize="12" Margin="24" MaxWidth="300"
                   HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
                   Text="Nothing in this directory." IsVisible="{Binding !HasRemoteEntries}" />

        <!--
          ============ THE INVITATION, which is what this screen opens on ============

          Everything the connect bar used to hold, in the half of the screen it was always about. It covers
          the pane from below the header to the bottom, opaquely: the breadcrumb, the column headings and
          the empty listing are all still laid out underneath, and all three are furniture for a listing
          that does not exist yet.

          Two steps, not one. The first is an invitation with a button — a name for what this screen does, a
          sentence about what it costs, and one thing to press — and the picker is the second. A combo box
          sitting open in the middle of an empty pane would be a form with no question above it; the reason
          the pane is empty is the question, and that is what the first step says.

          ◆ The two steps also keep this inside its budget, which is the pane's height less whatever the
          queue has taken — 268 pixels with three transfers on it. The sentence and the button go away when
          the picker arrives, so the tall shape is the form rather than form-plus-prose, and neither shape
          reaches the floor. Anything added here has to hold that; the layout suite measures both.
        -->
        <Border Grid.Row="1" Grid.RowSpan="5" Background="{StaticResource Canvas}"
                IsVisible="{Binding !IsConnected}">
          <StackPanel Spacing="10" Margin="20" MaxWidth="320"
                      HorizontalAlignment="Center" VerticalAlignment="Center">

            <!--
              The mark the hosts screen puts on a group, at the size an empty state can carry one. Still a
              hand-picked glyph rather than one of the Material Icons codepoints the file rows now use — this
              invitation predates the app's own IconFont and is outside wave C's restyle, which touched the
              pane headers, the rows and the TRANSFERS strip and not this card — and ▤ already means "a place
              things are kept" without borrowing from a font built for a different visual language.
            -->
            <Border Width="44" Height="44" CornerRadius="12" HorizontalAlignment="Center"
                    Background="{StaticResource Raised}" BorderBrush="{StaticResource Border}"
                    BorderThickness="1">
              <TextBlock Text="▤" FontSize="18" Foreground="{StaticResource TextDim}"
                         HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>

            <TextBlock Classes="heading" FontSize="16" TextAlignment="Center"
                       Text="Connect to a host" IsVisible="{Binding ShowsHostPicker}" />
            <TextBlock Classes="heading" FontSize="16" TextAlignment="Center"
                       Text="Open a bucket" IsVisible="{Binding ShowsBucketChoice}" />
            <!--
              A different heading rather than the same one over an empty picker, which is what this screen
              used to do: SELECT BUCKET opened a combo box with nothing in it and no sentence anywhere saying
              where a bucket comes from. From here that is indistinguishable from an application that cannot
              add one — and this is the screen somebody looking for S3 arrives at.
            -->
            <TextBlock Classes="heading" FontSize="16" TextAlignment="Center"
                       Text="No buckets yet" IsVisible="{Binding ShowsNoBuckets}" />

            <!--
              The sentence, and the button under it. Both go away once the picker is up — by then they have
              been read or they have not — which is why they are one collapsing group rather than two
              elements each answering the same question.

              The sentence is the one the empty pane used to carry, and it is kept rather than dropped
              because it is the one thing about this screen that surprises people: browsing files is a
              second login, and it shows up in the host's auth log as one. The bucket's counterpart says
              the opposite thing for the opposite reason — there is no session to open at all, so nothing
              is dialled until a listing is asked for.
            -->
            <StackPanel Spacing="10" IsVisible="{Binding !IsChoosingRemote}">
              <TextBlock Classes="hint" FontSize="12" TextAlignment="Center"
                         IsVisible="{Binding ShowsHostPicker}"
                         Text="Browsing a host's files opens its own SFTP connection, so the host records a second login — it is not the channel a terminal uses." />
              <TextBlock Classes="hint" FontSize="12" TextAlignment="Center"
                         IsVisible="{Binding ShowsBucketChoice}"
                         Text="A bucket has nothing to connect to: the keys and the endpoint are tested by the first listing, not by opening a session." />
              <!--
                Where a bucket comes from, said on the screen that needs one. It names the keychain rather
                than only offering a button, because the button is a shortcut and the sentence is what makes
                the bucket findable again next week without it.
              -->
              <TextBlock Classes="hint" FontSize="12" TextAlignment="Center"
                         IsVisible="{Binding ShowsNoBuckets}"
                         Text="A bucket is an item in your keychain — the bucket name, its keys and its endpoint, encrypted like everything else there. Add one and it appears here." />
              <Button Classes="accent" HorizontalAlignment="Center" Content="SELECT HOST"
                      IsVisible="{Binding ShowsHostPicker}"
                      Command="{Binding BeginChoosingRemoteCommand}" />
              <Button Classes="accent" HorizontalAlignment="Center" Content="SELECT BUCKET"
                      IsVisible="{Binding ShowsBucketChoice}"
                      Command="{Binding BeginChoosingRemoteCommand}" />
              <Button Classes="accent" HorizontalAlignment="Center" Content="+ BUCKET"
                      IsVisible="{Binding ShowsNoBuckets}"
                      Command="{Binding AddBucketCommand}"
                      ToolTip.Tip="Goes to the keychain with the bucket editor open." />
            </StackPanel>

            <!-- ==== The picker, once it has been asked for ==== -->
            <StackPanel Spacing="8" IsVisible="{Binding IsChoosingRemote}">

              <ComboBox HorizontalAlignment="Stretch" ItemsSource="{Binding Hosts}"
                        SelectedItem="{Binding SelectedHost}"
                        IsVisible="{Binding ShowsHostPicker}"
                        PlaceholderText="choose a host">
                <ComboBox.ItemTemplate>
                  <DataTemplate x:DataType="vm:HostRowViewModel">
                    <StackPanel>
                      <TextBlock Classes="mono" Text="{Binding Label}" FontSize="12"
                                 Foreground="{StaticResource Text}" />
                      <TextBlock Classes="mono" Text="{Binding Address}" FontSize="10"
                                 Foreground="{StaticResource TextFaint}" />
                    </StackPanel>
                  </DataTemplate>
                </ComboBox.ItemTemplate>
              </ComboBox>

              <!--
                Gated on there being one, not merely on this being the S3 screen. An empty picker is the
                thing that made this screen look like it could not add a bucket, and the invitation above
                now sends somebody to the keychain instead of opening this.
              -->
              <ComboBox HorizontalAlignment="Stretch" ItemsSource="{Binding Buckets}"
                        SelectedItem="{Binding SelectedBucket}"
                        IsVisible="{Binding ShowsBucketChoice}"
                        PlaceholderText="choose a bucket">
                <ComboBox.ItemTemplate>
                  <DataTemplate x:DataType="vm:ObjectStoreRowViewModel">
                    <StackPanel>
                      <TextBlock Classes="mono" Text="{Binding Label}" FontSize="12"
                                 Foreground="{StaticResource Text}" />
                      <TextBlock Classes="mono" Text="{Binding Description}" FontSize="10"
                                 Foreground="{StaticResource TextFaint}" />
                    </StackPanel>
                  </DataTemplate>
                </ComboBox.ItemTemplate>
              </ComboBox>

              <!--
                Only for a host bound to nothing, exactly as the hosts screen's box is — and it is a
                different box holding a different value. This connection authenticates separately, so a
                password typed to open a terminal was never offered here.
              -->
              <TextBox PasswordChar="•" Text="{Binding TypedPassword}" PlaceholderText="password"
                       IsVisible="{Binding SelectedHostAsksForAPassword}" />

              <StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Center">
                <Button Classes="accent" Content="{Binding ConnectLabel}"
                        Command="{Binding ConnectCommand}" IsEnabled="{Binding !IsBusy}" />
                <Button Classes="ghost" Content="CANCEL"
                        Command="{Binding CancelChoosingRemoteCommand}" />
              </StackPanel>

            </StackPanel>

            <!--
              The status line, in the state where the connected strip above is not on screen. Wrapped rather
              than trimmed, unlike its counterpart up there: what lands here is a refusal with a reason in
              it — a server's own sentence about why a connection did not open — and an ellipsis through the
              middle of that is a message that has been shown without being said.
            -->
            <TextBlock Classes="hint" FontSize="11.5" TextAlignment="Center" Text="{Binding Status}" />

          </StackPanel>
        </Border>

        <!--
          Two highlights rather than one, because refusing is worth showing. Something dragged over a
          disconnected pane has to say so under the pointer — a pane that lights up nowhere reads as a
          window that has stopped answering, and the answer arriving after the drop is the answer arriving
          too late. See the local pane for why neither may hit-test.
        -->
        <Border Grid.Row="0" Grid.RowSpan="6" IsHitTestVisible="False"
                Background="{StaticResource AccentWash}" BorderBrush="{StaticResource Accent}"
                BorderThickness="2" IsVisible="{Binding IsRemoteDropTarget}" />

        <Border Grid.Row="0" Grid.RowSpan="6" IsHitTestVisible="False"
                Background="{StaticResource DangerWash}" BorderBrush="{StaticResource DangerSoft}"
                BorderThickness="2" IsVisible="{Binding IsRemoteDropRefused}">
          <TextBlock Classes="hint" Text="Connect to a host first." FontSize="12"
                     Foreground="{StaticResource Danger}"
                     HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Border>

      </Grid>

    </Grid>

    <!--
      ============ The TRANSFERS strip ============
      ── v5b ─────────────────────────────────────────────────────────────────────────────────────────────
      DeepChrome and a top border, per SFTP.dc.html, in place of the plain Sidebar fill this used to carry —
      the same surface the session shell's own header and status bar are painted in, which is what fuses the
      strip into that frame rather than leaving it read as another pane. Two Borders share this row and
      exactly one is ever visible: the design's own header — TRANSFERS plus a count chip — with the queue
      underneath while there is one, and a single quiet line otherwise. That is the "collapse" wave C's own
      brief asks for: not zero height, because the honest sentence that used to live inside the full strip
      still has somewhere to be read, but far short of the full header-plus-rows shape.

      The design's own aggregate throughput readout — "8.4 MB/s" beside an upward arrow — is not drawn.
      TransferRowViewModel.Progress computes a rate per transfer, off TransferSnapshot.BytesPerSecond, and
      nothing anywhere in this queue sums those into one number for the whole strip; inventing one here would
      be exactly the fabricated fact this project's honesty rule forbids. Recorded as a deviation rather than
      silently dropped.
    -->
    <Border Grid.Row="1" Background="{StaticResource DeepChrome}" BorderBrush="{StaticResource Border}"
            BorderThickness="0,1,0,0" MaxHeight="196" IsVisible="{Binding HasTransfers}">
      <Grid RowDefinitions="Auto,Auto,*,Auto" Margin="16,12">

        <Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,*">
          <TextBlock Grid.Column="0" Classes="label" FontSize="10" Text="TRANSFERS" VerticalAlignment="Center" />
          <!--
            h18, radius 5, Chip fill with no border — the design's own count chip, distinct from Border.chip's
            usual bordered-and-unfilled shape elsewhere in this application. ActiveTransfersLabel is
            "N active", off TransfersViewModel.ActiveTransfers: queued counts as active there for the reason
            its own remark gives, which matches what this chip is naming.
          -->
          <Border Grid.Column="1" Height="18" CornerRadius="5" Background="{StaticResource Chip}"
                  Padding="8,0" Margin="10,0,0,0" VerticalAlignment="Center">
            <TextBlock Classes="mono" FontSize="10.5" FontWeight="Medium"
                       Foreground="{StaticResource TextFaint}" VerticalAlignment="Center"
                       Text="{Binding ActiveTransfersLabel}" />
          </Border>
        </Grid>

        <!--
          ◆ THE STATUS LINE, in the state where the remote pane has no room for one — unchanged in meaning
          from the strip this replaced, only in paint. Right-aligned beside the queue's own "one at a time"
          policy sentence, which stays for the reason it always did: the queue refuses to overwrite, and
          nothing else on this screen says so.
        -->
        <Grid Grid.Row="1" ColumnDefinitions="*,Auto" Margin="0,8,0,10">
          <TextBlock Grid.Column="0" Classes="mono" FontSize="10.5" Foreground="{StaticResource TextGhost}"
                     Text="one at a time · nothing lands at its final name until it is complete" />
          <TextBlock Grid.Column="1" Classes="hint" FontSize="11" Margin="16,0,0,0"
                     HorizontalAlignment="Right" VerticalAlignment="Center"
                     TextTrimming="CharacterEllipsis" TextWrapping="NoWrap"
                     Text="{Binding Status}" IsVisible="{Binding IsConnected}" />
        </Grid>

        <ScrollViewer Grid.Row="2">
          <ItemsControl ItemsSource="{Binding Transfers}">
            <ItemsControl.ItemTemplate>
              <DataTemplate x:DataType="vm:TransferRowViewModel">
                <!--
                  Per SFTP.dc.html: a 320-pixel ellipsized "source → destination" label, a slim progress track
                  and a right-aligned status word — see TransferRowViewModel.Label/StatusWord. STOP, RETRY/
                  RESUME and DISCARD are the app's own, kept per wave C's brief rather than dropped for having
                  no equivalent in the design's static mock: icon-only now, restyled to the pane headers' own
                  quiet-glyph idiom, with a tooltip carrying the word the button used to print.
                -->
                <Grid ColumnDefinitions="320,*,64,Auto" Margin="0,5" ColumnSpacing="14">
                  <TextBlock Grid.Column="0" Classes="mono" FontSize="12"
                             Foreground="{StaticResource TextDim}" VerticalAlignment="Center"
                             Text="{Binding Label}" TextTrimming="CharacterEllipsis" />

                  <ProgressBar Grid.Column="1" Classes="transferbar" Minimum="0" Maximum="100"
                               Value="{Binding Percent}" VerticalAlignment="Center"
                               ToolTip.Tip="{Binding Progress}" />

                  <TextBlock Grid.Column="2" Classes="mono transferstatus"
                             Classes.running="{Binding IsInProgress}" Classes.done="{Binding IsDone}"
                             Classes.failed="{Binding HasFailed}" FontSize="12" FontWeight="Medium"
                             Text="{Binding StatusWord}" TextAlignment="Right" VerticalAlignment="Center" />

                  <StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="4">
                    <Button Classes="paneicon" Width="22" Height="22" IsVisible="{Binding IsRunning}"
                            ToolTip.Tip="Stop this transfer"
                            Command="{Binding $parent[ItemsControl].((vm:TransfersViewModel)DataContext).CancelTransferCommand}"
                            CommandParameter="{Binding}">
                      <TextBlock FontFamily="{StaticResource IconFont}" FontSize="13" Text="&#xE5CD;" />
                    </Button>
                    <Button Classes="paneicon" Width="22" Height="22" IsVisible="{Binding CanRetry}"
                            ToolTip.Tip="{Binding RetryLabel}"
                            Command="{Binding $parent[ItemsControl].((vm:TransfersViewModel)DataContext).RetryTransferCommand}"
                            CommandParameter="{Binding}">
                      <TextBlock FontFamily="{StaticResource IconFont}" FontSize="13" Text="&#xE5D5;" />
                    </Button>
                    <Button Classes="paneicon danger" Width="22" Height="22" IsVisible="{Binding IsFinished}"
                            ToolTip.Tip="Discard this row"
                            Command="{Binding $parent[ItemsControl].((vm:TransfersViewModel)DataContext).DiscardTransferCommand}"
                            CommandParameter="{Binding}">
                      <TextBlock FontFamily="{StaticResource IconFont}" FontSize="13" Text="&#xE872;" />
                    </Button>
                  </StackPanel>
                </Grid>
              </DataTemplate>
            </ItemsControl.ItemTemplate>
          </ItemsControl>
        </ScrollViewer>

        <Button Grid.Row="3" Classes="ghost" Content="CLEAR FINISHED" Margin="0,10,0,0"
                HorizontalAlignment="Left" Command="{Binding ClearCompletedCommand}" />

      </Grid>
    </Border>

    <!--
      The collapsed shape: no header, no count chip, no rows — just the honest sentence the full strip
      carried before anything was ever queued, kept per wave C's own brief rather than dropped along with the
      rest of the strip's furniture.
    -->
    <Border Grid.Row="1" Background="{StaticResource DeepChrome}" BorderBrush="{StaticResource Border}"
            BorderThickness="0,1,0,0" Padding="16,12" IsVisible="{Binding !HasTransfers}">
      <TextBlock Classes="hint" FontSize="11.5" Foreground="{StaticResource TextFaint}"
                 Text="Nothing queued. Choose a file in either pane and press the arrow pointing the way you want it to go." />
    </Border>

    <!--
      First contact and a changed key, over the whole screen. The same two refusals a terminal makes, and
      they arrive here on their own because this is a separate connection — a host trusted for a shell is
      trusted for this too, but a host nobody has connected to at all is met here first.
    -->
    <Border Grid.Row="0" Grid.RowSpan="2" Background="{StaticResource Canvas}"
            IsVisible="{Binding HasPendingHostKey}">
      <Border Classes="card">
        <StackPanel Spacing="12">
          <TextBlock Classes="heading" Text="Check this host's fingerprint" />
          <TextBlock Classes="hint"
                     Text="This host has not been seen before. Compare the fingerprint with what the operator published. Trusting it here also trusts it for terminals, and on your other machines." />
          <SelectableTextBlock Classes="mono" FontSize="12" Foreground="{StaticResource Text}"
                               TextWrapping="Wrap" Text="{Binding PendingHostKey.Fingerprint}" />
          <StackPanel Orientation="Horizontal" Spacing="8">
            <Button Classes="accent" Content="TRUST AND CONNECT" Command="{Binding TrustHostKeyCommand}" />
            <Button Classes="ghost" Content="CANCEL" Command="{Binding RejectHostKeyCommand}" />
          </StackPanel>
        </StackPanel>
      </Border>
    </Border>

    <Border Grid.Row="0" Grid.RowSpan="2" Background="{StaticResource Canvas}"
            IsVisible="{Binding HasHostKeyMismatch}">
      <Border Classes="card" BorderBrush="{StaticResource DangerSoft}">
        <StackPanel Spacing="12">
          <TextBlock Classes="heading" Text="The host key has changed" Foreground="{StaticResource Danger}" />
          <TextBlock Classes="hint" Text="{Binding HostKeyMismatch}" />
          <TextBlock Classes="hint"
                     Text="Nothing was connected, and there is no way to continue from here. If the server was legitimately rebuilt, edit the host on the Hosts screen and choose Forget host key." />
          <Button Classes="ghost" Content="CLOSE" Command="{Binding RejectHostKeyCommand}"
                  HorizontalAlignment="Left" />
        </StackPanel>
      </Border>
    </Border>

  </Grid>

</UserControl>
