diff --git a/docs/manual-checks.md b/docs/manual-checks.md
index 2c4467d..cc3a96a 100644
--- a/docs/manual-checks.md
+++ b/docs/manual-checks.md
@@ -269,33 +269,32 @@ single-process test can reach.
Open the hosts screen without creating any group.
-**Pass:** the grid is the flat wrap of host cards it always was — no GROUPS section above it, no headings
-between the cards, no UNGROUPED, nothing saying the hosts are unfiled.
+**Pass:** the grid is the flat wrap of host cards it always was — no GROUPS section above it, no chip on any
+card, nothing saying the hosts are unfiled.
-**Failure means:** the "invisible until used" property is gone, and every existing user gets a heading they
-did not ask for. `RebuildSidebarRows` returns early when `Groups` is empty; that early return is the feature.
+**Failure means:** the "invisible until used" property is gone, and every existing user gets a section they
+did not ask for. `HasGroups` is what hides the row of group cards; `HostRowViewModel.HasGroup` hides the chip.
-### 3.2 Filing hosts, and folding a heading
+### 3.2 Filing hosts, and reading which group a card is in
-Make two groups, file some hosts into each through the host editor, then click a heading.
+Make two groups and file some hosts into each through the host editor.
-**Pass:** the heading's chevron flips and its hosts disappear; the count on the heading does not change,
-because it counts what is in the group rather than what is on screen. Clicking again brings them back.
-
-**Also check:** clicking a heading does not change which host is selected — the drawer stays open on the
-same machine and its EDIT and DELETE go on acting on it. This is asserted in a test, but the test drives the
-view model directly; what it cannot see is whether the `ListBox` writes something else back through the
-binding first.
+**Pass:** every filed card carries the group's name as an accent chip under its address; the cards stay one
+flat wrap, in one order, with nothing between them. **There is no heading and no fold on the desktop** — the
+headings, their chevrons and UNGROUPED are the phone's, whose list has no room for a row of group cards.
**Then press a group card.** The grid narrows to that group's hosts, the card is marked as chosen, and
SHOW ALL appears beside GROUPS. Pressing it brings the rest back and unmarks the card.
+**Failure means:** a full-width bar with a chevron between the cards is the old grouping coming back through
+`SidebarRows` — the desktop grid binds `VisibleHosts`. See `HostsScreen.axaml`.
+
### 3.3 Deleting a group with hosts in it
Select a group with hosts and press DELETE.
**Pass:** the question names how many hosts are filed under it and says they stay. Agreeing removes the
-group; the hosts reappear under UNGROUPED with everything else about them unchanged.
+group; the hosts lose their chip and are otherwise unchanged.
**Failure means:** if the hosts vanish, the delete is rewriting host payloads, which it must not — see
`HostGroupRepository`.
@@ -305,8 +304,8 @@ group; the hosts reappear under UNGROUPED with everything else about them unchan
Make a group on machine A, file a host into it, sync. On machine B, sync, then delete the group and sync
again. Back on A, sync.
-**Pass:** A shows the host under UNGROUPED. Open that host's editor: the group picker shows "(a group that is
-no longer here)" and *keeps it selected*. Change the port and save.
+**Pass:** A shows the host with no chip on its card, as though it were in no group. Open that host's editor:
+the group picker shows "(a group that is no longer here)" and *keeps it selected*. Change the port and save.
**Failure means:** if the picker opened on "No group", saving has just unfiled the host — quietly, as a side
effect of an unrelated edit. That is the case `BuildGroupChoices` adds the placeholder for.
@@ -642,33 +641,45 @@ Start 7.1's slow connection and press GIVE UP (or the tab's cross) while it is s
opened — it is a real shell, and one running with nothing naming it would be worse than one that comes
back.
-### 7.6 Dragging a host into a group · **least covered, like all drag and drop**
+### 7.6 Dragging a host onto a group card · **least covered, like all drag and drop**
-Make two groups and file a host into one. Drag a host card onto another group's heading; onto a host card
-inside another group; and onto UNGROUPED.
+Make two groups and file a host into one. Drag a host card up onto the other group's card.
-**Pass:** whatever is under the pointer washes accent while the pointer is over it, the cursor shows a move
-rather than a refusal, and the drop files the host — it moves under that heading and the counts on both
-headings change. Dropping onto its own group's heading is refused while still in the air.
+**Pass:** the group card under the pointer takes a two-pixel accent border while the pointer is over it, the
+cursor shows a move rather than a refusal, and the drop files the host — the chip on its card changes to the
+new group's name, the host counts under both group cards change, and the status line says where it went.
-**Failure means:** headless Avalonia cannot synthesise a platform drag, so nothing about this gesture is
-automated. The write it performs is: `MovingAHostToAGroup_FilesItAndLeavesItSelected`.
+**Also check three refusals**, each of which must show the "no" cursor and mark nothing: over the card of the
+group the host is *already* in; over another **host** card, which is deliberately not a target now that there
+are no headings to say which group it would mean; and over the empty space around the cards.
+
+**And getting a host back out** is the host's own editor — pick "No group" in its picker. There is no
+UNGROUPED target on the desktop any more, because there is no UNGROUPED heading for it to be.
+
+**And a drag held near the top or bottom edge of the grid scrolls it**, which is what makes this usable at
+all with forty machines: the group cards are the first thing in the scroller, and a drag cannot use the
+wheel. The pointer has to keep moving inside the band — a stationary pointer gets no drag events.
+
+**Failure means:** headless Avalonia cannot synthesise a platform drag, so the picking up, the cursor and
+the drop are covered by nothing. What *is* automated is the decision each drag event takes —
+`HostGridTests.TheGroupCardsAreWhatAcceptsADroppedHost` raises a real `DragOver` over both kinds of card —
+and the write at the end, `ShellFlowTests.MovingAHostToAGroup_FilesItAndLeavesItSelected`.
### 7.7 A click still selects, and a double click still connects
-Click host cards; drag one a few pixels without releasing; double-click one. Then double-click a group
-heading.
+Click host cards; drag one a few pixels without releasing; double-click one. Then double-click the space
+around the cards.
-**Pass:** a click selects, a small movement starts nothing, and a double click connects. Double-clicking a
-heading folds it and unfolds it again and connects to nothing.
+**Pass:** a click selects, a small movement starts nothing, and a double click connects. Double-clicking the
+space around the cards connects to nothing.
**Failure means:** the 5-pixel threshold in `HostsScreen.axaml.cs` is not doing its job — the same failure
-as 2.16 on the other screen, and here it would make the grid unusable. A heading that connects means the
+as 2.16 on the other screen, and here it would make the grid unusable. Empty space that connects means the
double-tap handler has lost its check that the pointer was over a card.
### 7.8 The highlight clears after a drag that goes nowhere
-Drag a host over a heading and release outside the grid, or press Escape mid-drag.
+Drag a host over a group card and release outside the grid, or press Escape mid-drag.
**Pass:** the wash goes away.
@@ -676,7 +687,7 @@ Drag a host over a heading and release outside the grid, or press Escape mid-dra
With host A selected, right-click host B and choose Delete.
-**Pass:** the question names **B**. Then right-click a group heading.
+**Pass:** the question names **B**. Then right-click the space around the cards.
**Pass:** no menu opens at all, and the host selection has not moved.
diff --git a/src/DodoSSH.Client.App/App.axaml b/src/DodoSSH.Client.App/App.axaml
index 8ad5b88..80ad3eb 100644
--- a/src/DodoSSH.Client.App/App.axaml
+++ b/src/DodoSSH.Client.App/App.axaml
@@ -276,9 +276,12 @@
-
+
-
-
+
+
@@ -198,10 +207,18 @@
SelectedItem is GroupFilter and not SelectedGroup. The two are nearly the same thing here —
GroupFilter assigns SelectedGroup — but only one of them may narrow the grid; see the property
for why the phone's own use of SelectedGroup must not.
+
+ ◆ THESE CARDS ARE THE DROP TARGET. A host card dragged onto one is filed under that group, and
+ that is the whole of what a drag does on this screen. It used to be a heading inside the host
+ grid, and the headings are gone — see the note on the host grid below. A group is a thing that
+ is drawn once, at the top, where the eye already goes to narrow the grid; a target that is also
+ the label of what you are aiming at needs no explaining. See the code-behind for the gesture.
-->
+ SelectedItem="{Binding GroupFilter}"
+ ToolTip.Tip="Press a group to show only its hosts. Drag a host card onto one to file it there.">
@@ -257,15 +274,31 @@
focus off the terminal's child window and then calls Focus() on something that refuses it,
leaving the window with nothing focused and the keystrokes going nowhere.
- It binds SidebarRows rather than VisibleHosts, so the group headings are still in it. Two
- reasons, and neither is decoration: a grid showing every group at once has to say which cards
- belong to which, and a heading is the drop target that takes a host out of one group and into
- another. See VaultViewModel.RebuildSidebarRows for why a heading is a row rather than a
- container, and the code-behind for what a drag does with one.
+ ◆ CARDS ONLY, AND NO HEADINGS. It binds VisibleHosts rather than SidebarRows, which is the
+ collection with the group headings folded into it. Those headings were a full-width fold-away
+ bar between the cards — a chevron, a name and a count — and in a wrap of cards they read as a
+ collapsed dropdown that had somehow been dealt the hosts: the moment a first group existed the
+ grid stopped being a grid. Two controls saying "group" on one screen was one too many, and the
+ cards above are the better of the two. SidebarRows and the fold are still the phone's, whose
+ list has no room for a row of group cards; see Android's HostsScreen.
+
+ What a heading also was is the drop target, and that moved to the group cards rather than
+ going away with it — see the note on them above, and the code-behind for the gesture. A host is
+ taken back out of a group through its own editor, which is the one place "no group" can be said
+ plainly.
+
+ Which group a card is in is on the card, as a chip. That is what the heading above it used to
+ say, and one chip per card says it without an accordion.
+
+ SelectedItem is still SelectedSidebarRow and deliberately not SelectedHost, though every item
+ here is now a host. That property swallows the null the ListBox writes back whenever the
+ collection is rebuilt — which is every filter keystroke and every background sync — where
+ binding the host selection directly would let that null through, and a null host selection
+ closes the drawer and disarms a delete somebody is in the middle of confirming. See the
+ property, and RebuildVisibleHosts on where the real clearing is done.
-->
@@ -279,7 +312,8 @@
one per card: a ContextMenu inside a DataTemplate would have the row for its data context,
and the commands here are the vault's. The code-behind selects whatever was right-clicked
before the menu opens, so the selection-based commands act on the card under the pointer,
- and cancels the menu outright over a group heading, which has no host to act on.
+ and cancels the menu outright over the space around the cards, where there is no host to act
+ on.
-->
@@ -290,35 +324,9 @@
-
+
-
-
-
-
-
-
+
@@ -389,6 +397,26 @@
VerticalAlignment="Top" Margin="6,3,0,0" />
+
+
+
+
+