Four of them. A screen claims the ones it needs with <Layout.Pane column="…"> and leaves the rest unmounted.
The product’s own navigation. Not part of any one screen — it is how you get between them.
Always the conversation. A hub before you have picked one, a chat once you have. The breadcrumb is the way back.
Belongs to whatever occupies main, and comes with it. Every row is a door into the work area.
Mounted only when something is open. Opened by a row in the context column, by Odin from inside a chat, or by a chat that starts about a thing.
Chrome belongs in the pane’s card snippet, never in the body. A pane that draws its own frame stops matching the ones beside it the moment either changes.
Who owns a column, who owns a width, and what a breakpoint is allowed to measure.
Two components cannot claim the same column — registerPaneTrack overwrites the owner. So a route that takes main must leave CHAT_MAIN_ROUTES, and the shared chat pane then remounts whenever you cross between it and a route still on the list.
Card has no width prop. It fills its parent, and the width comes from the pane’s grid column or a dialog’s size tier. width: 480px on body content is a named anti-pattern: it puts a layout decision inside a component that cannot see the layout.
One number, 1100px, and it is a container query on .layout-pane rather than a media query on the window. Two panes in the same window can sit on opposite sides of it.
The spacing page says layout widths are not spacing steps. Pane widths come off that scale anyway. One of the two documents has to give.
Once in layout.group-pane.svelte and once in layout.group-collapse-button.svelte, kept in step by a comment. Nothing enforces it.
main changes when you change conversation. work-area opens and closes beside it while that conversation stays where it is.
So Odin can open something mid-chat without costing you your place, and closing the work area gives back the context column you had.
Closing the work area is not a way back to the hub. If you were in a chat you are still in that chat, because main never moved.
A hub is how you start or return to the conversations about the thing you have open. Opening a plan, a design kit or a dashboard lands you on its hub rather than inside one of its tools.
Before hubs, opening a design kit landed on a contents table and opening a plan landed on a calendar. That picks a tool for you, hides the rest, and leaves the conversations about the thing nowhere you can find them again.
The entity’s root route is the hub. Whatever used to live there moves down a level and keeps working — a design-kit item opened from a chat still needs somewhere to land.
Each occupant of main brings its own context column. They are different columns with different rules, and reading them as one column with changing contents gets the behaviour wrong.
The same three rows every time you open that kind of thing. Written into the entity type, not gathered.
Whatever this conversation has touched. Different in every chat, and longer at the end of one than at the start.
Every row in either one is a door into the work area.
aside-right and work-area can be shown together — the layout allows it. The hubs choose to swap them. That is a product decision and should not be repeated as though the layout forced it.
A hub anchors its composer to a URN and filters its table on the same one. Entities have one: a plan is urn:focause:plan:{id}, and everything follows.
Sports, Content and responsible-gaming-as-an-area do not. They are regions of the product rather than things, and nothing in the identity scheme mints an aggregate for them. Two workarounds have already shipped and they answer the question differently.
The section lists its children’s conversations — selectChatRowsForContexts(chats, reportUrns). The section anchors nothing itself, and the table reads as “conversations about your dashboards”.
An invented frontend-only URN, urn('area', 'content'). Nothing on the backend mints or reads it. A chat created with it comes back in this hub’s table and nowhere else.
You arrive at a contact because something pointed you there, you look, and you leave. Nobody holds a conversation about a person, and a nav entry opening a searchable list of every contact invites browsing a customer database — not a job Odin is for.
A hub is for things people talk about. That test is worth applying before the URN question is even asked.
#4238 asks whether an area can carry a chat context at all, and it may be answered with a no. Minting urn:focause:area:sports would put a navigation grouping into an identity scheme that is load-bearing across the backend, the agent and the warehouse.
A route change is choreographed rather than instant. Everything derives from --layout-total-duration, which is 700ms multiplied by the product-wide motion scale — the same setting the motion page documents. Turn motion down and the layout slows with everything else; turn it off and it stops.
The fractions are constrained, not free: the old content must be gone before the panes start, and the four have to add to one. Content travels 5px, which is enough to read as movement and not far enough to look like a slide.
The variables sit on :root because they have to reach the ::view-transition-* pseudo-elements, which are anchored there rather than inside the grid.
registerPaneTrack overwrites the column owner. Two things cannot claim main, which is the whole of #4219.
It gives you ../design/x, which is what an href wants and is useless against page.url.pathname. Comparing them silently leaves the work area mounted and suppresses the context column. Compare against a literal path.
The body is overflow: hidden, so a hub column needs a ScrollArea or everything below the fold is cut off with no scrollbar to tell you.
Proposals carry a planId, so nothing can be proposed about a design kit yet. Render the section anyway — it is where entity proposals will land.
No card carries a count and nothing in it is a target. There is no source for those numbers, and one invented to fill the space would have the surface arguing from evidence that does not exist. A step gains a click and a count together.
These are open in the tree, not open in the writing. The page will say so until they close.
Two have closed already. CMS, localization and files did merge, and it is live. And #4253 — write the real layout-patterns document — is this page.
A route cannot take over main and keep the shell’s shared chat pane. Leaving CHAT_MAIN_ROUTES remounts the chat — thread, SSE session and scroll all reset. The cost grows with every hub, because the shared pane is only shared across the routes still on the list.
Can an area carry a chat context at all? Sports, Content and RG-as-an-area are regions of the product, not things, and nothing mints a URN for them. It may be answered with a no.
Is the responsible-gaming hub per alert or per area.
The layout package’s SPECS.md has not been read into this document. Column widths, what aside-left holds, how many panes a screen may have, and the z/ ladder between below and toolpanel are all still described from the outside.