@layer components {
  /* Menus

     One popup language for the whole product, and there are eight of them now:
     status, the two people, project, role, the workspace, you, the views and
     the lens. All popovers, so light dismiss, Escape and the top layer are the
     browser's job rather than eight more bits of JavaScript — and all wired to
     the menu controller, which is where the rest of what a menu does lives:
     opening onto the item you are already on, the arrow keys, and giving the
     focus back to whatever opened it. */

  .menu[popover] {
    position: fixed;
    margin: 0;
    min-width: 15rem;
    padding: var(--space);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-card);
    overflow: visible;
  }

  /* One menu is wider than the rest, and it is the Pounce menu.

     15rem is the right width for a list of one-word destinations and the wrong
     one for the section under its rule, where every row is a title somebody
     typed — a task called "Reconcile the Q3 print invoices" against "Inbox".
     At the shared width almost every favourite ended in an ellipsis, which is
     a shortcut you cannot read.

     A floor and a ceiling rather than a fixed width: the menu still shrinks to
     the vocabulary above the rule when nobody has starred anything, and it
     stops at the point where a popover anchored to the bottom-left corner
     would run off the right of a phone. The ceiling is arithmetic off the same
     two tokens .menu--above uses to place it, for the reason written under
     .menu--under-masthead — a number typed twice is two numbers waiting to
     disagree. */
  .menu[popover].menu--wide {
    min-width: 21rem;
    max-width: calc(100vw - var(--space-3) - var(--safe-left) - var(--space-3));
  }

  /* And the rows in it give rather than the panel, past that ceiling. Same
     rule and same reason as .menu__panel .menu__title below: a menu as wide as
     the longest thing anybody has ever called a task is not a menu.

     min-width: 0 on the title, because a flex item will not shrink below its
     own content without it — and `margin-right: auto` on .menu__title makes it
     the item that would otherwise refuse. */
  .menu--wide .menu__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* A menu is never taller than the window it opens in.

     Every list in here is as long as the workspace made it — a project per
     project, a face per member, a word per tag — and none of them had a
     ceiling. The panels inside the number's menu have had one all along
     (.menu__panel, below); the menus that are only a list never did, and a
     popover is in the top layer with `position: fixed`, so the rows past the
     bottom of the window were not scrolled to, they were simply not reachable.
     A workspace of sixty people is 2,600px of assignee menu in a 900px window.

     Three caps rather than one, because a menu is only as tall as the room the
     thing it hangs off leaves it, and each of the three anchorings knows a
     different answer. They are arithmetic off the same tokens that place them,
     for the reason written under .menu--under-masthead: a number typed twice
     is two numbers waiting to disagree.

     Not the stacked menu: its own frames carry the surface and its panels cap
     themselves, so a cap out here would scroll the card rather than the list.

     overscroll-behavior so that reaching the end of a menu does not hand the
     wheel to the list of a hundred tasks behind it. */
  .menu[popover]:not(.menu--stacked) {
    max-height: calc(100dvh - var(--space-3));
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* Opening, and — this is the new half — closing.

     All eight of these used to arrive politely on a keyframe and then blink out
     of existence, because a keyframe only runs one way and there was nothing on
     the other side of it. A popover can be transitioned in both directions now:
     `display` and `overlay` with allow-discrete hold the element and its place
     in the top layer for the length of the transition rather than dropping both
     on the first frame, and @starting-style gives the entry the from-state the
     keyframe used to carry.

     So the keyframe is gone and one pair of rules does both ways. Leaving is on
     the exit clock, which is shorter and steeper — nobody needs to watch a menu
     they have finished with. Under reduced motion --transition is 0s and the
     menu simply is open, which is the same answer the rest of the product
     gives; no branch needed here. */
  .menu[popover] {
    /* The column and the gap live here rather than in :popover-open, and the
       reason is the closing half above. `display` is held at flex for the
       length of the exit transition by allow-discrete, but nothing holds the
       properties that were sitting beside it: the moment the menu closes,
       :popover-open stops matching and flex-direction snaps back to its
       initial `row`. The menu is still on screen for those 150ms, so every
       item in it flung itself into a horizontal line and then faded out.

       Only `display` has to be conditional — an author `display: flex` on the
       base rule would beat the UA sheet's `display: none` and leave every menu
       in the product permanently open. The rest of the layout has no effect
       while the element is not displayed, so it is safe out here, and being
       out here is what makes closing look like the reverse of opening. */
    flex-direction: column;
    gap: 2px;

    /* A fade and nothing else. It used to rise eight pixels into place and
       sink back out, which is the kind of thing that is invisible on one menu
       and an accumulated tax across the eight of them. Opacity is what a panel
       opening over a page looks like. */
    opacity: 0;
    transition: opacity var(--transition-exit),
                overlay var(--transition-exit) allow-discrete,
                display var(--transition-exit) allow-discrete;
  }

  .menu[popover]:popover-open {
    display: flex;
    opacity: 1;
    transition: opacity var(--transition),
                overlay var(--transition) allow-discrete,
                display var(--transition) allow-discrete;

    @starting-style { opacity: 0; }
  }

  .menu::backdrop { background: light-dark(rgb(25 26 29 / 8%), rgb(0 0 0 / 40%)); }

  /* A menu drawn as two panels rather than one.

     The popover keeps the positioning, the transition and its place in the top
     layer; the surface moves to the panels inside it, so what you see is two
     separate cards with the page between them. `.menu[popover].menu--stacked`
     and not `.menu--stacked`, because the base rule is a class and an
     attribute and anything shorter loses to it silently — the note in
     forms.css is about exactly this. */
  .menu[popover].menu--stacked {
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
    gap: var(--space);
  }

  .menu--stacked .menu__frame {
    display: flex;
    flex-direction: column;
    /* Each card is its own width. Stretched, the line of four would grow to
       whatever a panel below it needed, which is fixed-width columns and then
       a gap of nothing on the right of them. */
    align-self: flex-start;
    gap: 2px;
    padding: var(--space);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-card);
  }

  /* The number's line: the two tiles, then the two that open a menu.

     Every column is a fixed width, and that is the whole point of the rule.
     They were content-sized — `auto` for the tiles, `minmax(8rem, 14rem)` for
     the dropdowns — which meant no two of the four agreed on a width and none
     of them agreed with itself: Today was narrower than Done until it said
     "Not today" and became wider, and a dropdown was as wide as whatever the
     task happened to be filed under. So the panel was a different size and
     shape on every row you opened it from, and pressing one of the dropdowns
     moved the thing you had just been reading. Four buttons that do four
     comparable things should be two pairs of one size, and the size should not
     depend on the words.

     The tile holds "Not today" — the longest either of the pair ever says —
     inside its own padding, and the two dropdowns take the same width as each
     other, wide enough for a project name and narrow enough that the line
     stays under a phone's width before the wrap below.

     `.menu__frame.rowactions`, because the frame rule above it is
     `.menu--stacked .menu__frame` and a bare `.rowactions` is a class short of
     it — it loses `display`, the buttons stack, and the panel shrinks to the
     widest of them. Third time in this file's neighbourhood; the note in
     forms.css is the standing warning. */
  .rowactions {
    --rowaction-tile: 7rem;
    --rowaction-pick: 12rem;
  }

  .menu--stacked .menu__frame.rowactions {
    display: grid;
    grid-template-columns: repeat(2, var(--rowaction-tile)) repeat(2, var(--rowaction-pick));
    gap: var(--space);
  }

  .rowaction {
    display: flex;
    /* The icon over the word, which is what makes these two read as a pair of
       buttons rather than as two more menu rows. Everything else in a menu is
       a row and puts its icon beside its title; these are the two things you
       came here to press, and they are the only place in the product where a
       picture is allowed to lead. */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space);
    /* Tall enough to hold both without either being crowded: 20px of icon, the
       gap, and a line of type, inside the panel's own padding. */
    min-height: 5rem;
    padding: var(--space) var(--space-2);
    border: 1.5px solid var(--line-strong);
    /* Concentric with the panel these two fill, not --radius-sm — see
       --radius-inset in _tokens.css. Their own border is 1.5px and drawn, so
       where a menu row only pinched the gap, these two put a visibly squarer
       corner a few pixels inside a visibly rounder one. */
    border-radius: var(--radius-inset);
    background: var(--surface);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    color: var(--ink);
    text-align: center;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition),
                color var(--transition), var(--responds);
  }

  .rowaction:hover { background: var(--surface-hover); border-color: var(--ink-faint); }

  /* Full strength, unlike a menu row's. An icon at 0.75 beside a title is quiet
     furniture; the same icon leading a button, in Done's green, just reads as a
     button that has been disabled. */
  .rowaction .menu__icon { opacity: 1; }

  /* Done is the one that closes the task, and it is the only thing in this menu
     that says so in a colour. Held back to the border and the ink rather than a
     filled button: the four are peers, and one of them painted solid green
     would be the loudest thing on a page of quiet rows. */
  .rowaction--done { color: var(--go); border-color: var(--go); }
  .rowaction--done:hover { background: var(--go-sunk); border-color: var(--go); }

  .rowaction[hidden] { display: none; }

  /* The two in that line that open another menu rather than doing something.

     The same box and the same height as the two tiles beside them, and wider,
     because they hold a value rather than a word — which is a dropdown's shape
     and not a tile's. Wider by a fixed amount: the two of them are one width
     and the two tiles are another, both set in the grid below rather than by
     what the words happen to be. The label over the value is the detail
     page's property, set the same way, because it is the same two decisions
     being made in a different place. The chevron is the picker's own and says
     the press goes somewhere rather than happening here.

     No icon. The pair beside these lead with a picture because that is what
     makes them read as buttons; a control that has a word for a label and a
     value under it does not need a third thing to be recognised by. */
  .rowaction--picks {
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: 2px;
    padding-inline: var(--space-2);
    font-weight: var(--weight-normal);
    text-align: left;
  }

  /* The type comes from base.css with every other small-caps label in the
     product; this is only what makes it a line with a chevron at the end. */
  .rowaction__label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space);
  }

  /* A tag line runs to whatever somebody has typed, so this is the one thing
     in these menus allowed to end in an ellipsis rather than widen the panel.
     min-width: 0 because a flex item will not shrink below its own content
     without it, which is the whole of why this needs to be said. */
  .rowaction__value {
    min-width: 0;
    font-size: var(--text-base);
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Nothing filed there yet — see rowmenu#say. The same tint the pickers on
     the detail page use for the same sentence, because it is the same
     sentence. */
  .rowaction__value[data-blank] { color: var(--ink-faint); }

  /* The list a dropdown opens, inside the menu it was pressed in rather than
     instead of it.

     It is a .menu__frame like the line above it, so it is the same card in the
     same stack: what changes when you press Project is that a second card
     appears under that button, not that the menu you were reading is replaced
     by a differently shaped one. The gap between them is the stack's own.

     Placed by rowmenu#unfold, which measures the dropdown and writes the two
     values here: the panel is that button's width and starts at its left edge,
     the way a select's list is the width of the select. align-self, or a flex
     item stretches to the menu and the alignment says nothing.

     A tall list scrolls inside the panel rather than growing the menu past the
     bottom of the window — the workspace decides how many projects there are,
     and one menu cannot be as tall as the answer. */
  /* `.menu__panel[hidden]`, and not a bare `[hidden]`: the frame rule above
     sets `display` in a class-and-descendant selector, which beats the UA
     sheet's `[hidden] { display: none }` and left a shut panel on the screen.
     Fourth time in this file; the note in forms.css is the standing warning. */
  .menu__panel[hidden] { display: none; }

  .menu__panel {
    width: max-content;
    min-width: var(--panel-w, var(--rowaction-pick));
    max-width: 20rem;
    margin-inline-start: var(--panel-x, 0px);
    max-height: min(20rem, 50vh);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* A name longer than any of that. The panel starts at its dropdown's width
     and grows to the words rather than truncating them at a width the words
     had no say in — a list whose current row reads "Kellner reb…" under a
     button reading "Kellner rebrand" is a list that has been squeezed. Past
     20rem the row is what gives, because a menu as wide as the longest thing
     anybody has ever named a project is not a menu. */
  .menu__panel .menu__title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Held down for as long as its panel is open, so the line says which of the
     two you are reading. The same surface a hover gives, because it is the
     same statement about the same button; the chevron flipping is picker.css's
     [aria-expanded="true"], which this shares with every other picker in the
     product. */
  .rowaction[aria-expanded="true"] {
    background: var(--surface-hover);
    border-color: var(--ink-faint);
  }

  /* Three, when the task offers no pull: Today is taken off the panel rather
     than left there dead, and the three that remain close the gap. Done keeps
     the width it had beside Today rather than growing into the space, because
     the panel should not change shape between two rows of the same list. */
  .menu--stacked .menu__frame.rowactions:has(.rowaction[hidden]) {
    grid-template-columns: var(--rowaction-tile) repeat(2, var(--rowaction-pick));
  }

  /* Four of these in a line is wider than a phone, and the column that would
     give is the one holding the words. So the line becomes two: the tiles
     side by side, then a dropdown each, which stop being tile-height because
     nothing beside them is asking them to be. */
  @media (width <= 48rem) {
    /* Two equal halves rather than the fixed pair above: the panel is being
       asked to fit a narrow window, and the tiles are what gives. They are
       still the same width as each other, which is what was being asked for. */
    .menu--stacked .menu__frame.rowactions,
    .menu--stacked .menu__frame.rowactions:has(.rowaction[hidden]) {
      grid-template-columns: 1fr 1fr;
    }

    .rowaction--picks {
      grid-column: 1 / -1;
      min-height: 0;
    }
  }

  /* The one place a menu takes typing rather than a press: the tag picker,
     where the whole point of the control it replaced was that you could name
     something that did not exist yet. Under a rule and set on the sunk paper,
     so it reads as a different kind of act from the rows above it rather than
     as one more row that happens to have a box round it. */
  .menu__foot {
    display: flex;
    margin-top: var(--space);
    padding-top: var(--space);
    border-top: 1px solid var(--line);
  }

  /* The search inside a menu, and it is the shared field at the menu's scale:
     the same sunk paper, the same faint line, the same lift to white on focus.
     What it does not take is the field's height or its corner — it fills a
     padded panel edge to edge, so it is drawn at --radius-inset like every
     other thing that does (see the note in _tokens.css), and it sits at the
     44px floor rather than the 52px a form uses, because a menu row beside it
     is 44px too. */
  .menu__input {
    font: inherit;
    font-size: var(--text-base);
    width: 100%;
    min-height: var(--control-h-sm);
    padding: 0 var(--space);
    border: 1.5px solid var(--line);
    border-radius: var(--radius-inset);
    background: var(--surface-sunk);
    color: var(--ink);
    transition: background var(--transition), border-color var(--transition), var(--responds);
  }

  .menu__input:hover { border-color: var(--line-strong); }

  .menu__input::placeholder { color: var(--ink-faint); }
  .menu__input:focus { background: var(--surface); border-color: var(--accent); }

  /* A menu holds two sizes and no third.

     --text-xs in caps for the furniture — the label at the head of a panel and
     the count at the end of a row, both of which are the system talking about
     the list. --text-base for every row you can press. That is it.

     It used to hold three, and all three were wrong against each other: the
     rows were --text-md at --weight-medium, the quiet rows under the rule were
     --text-sm, and the label was --text-xs. Opening the workspace menu meant
     reading 22px, then 16px, then 15px down a single 240px column, and the
     jumps were doing no work — nothing about Settings is more important than
     Switch workspace by the ratio of 22 to 16. The size was carrying an
     emphasis the product never meant.

     --text-base rather than --text-md because that is what everything else
     that asks you to pick one of a set is already set at: the palette's
     results, the picker in a form, the Pounce button that opens the biggest of
     these menus. --text-md is the row-title size — a task, a member, a project,
     the workspace's own name in the masthead. A menu row is not one of those;
     it is a choice about them.

     And --weight-normal, because the weight is spoken for. Bold in a menu means
     the row you are already on (aria-current, below), and a resting state at
     500 leaves that with only colour to say it with. */
  .menu__item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--control-h-sm);
    padding: 0 var(--space-2);
    border: 0;
    /* The row is the width of the panel's inside, so its top and bottom
       corners are drawn against the panel's own — --radius-inset, which is
       the radius that keeps the gap even round them (_tokens.css). */
    border-radius: var(--radius-inset);
    background: none;
    font: inherit;
    font-size: var(--text-base);
    font-weight: var(--weight-normal);
    letter-spacing: var(--tracking-tight);
    color: var(--ink);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--transition), color var(--transition), var(--responds);
  }

  /* Solid ink resting, and the tint is what the hover says. The rows were
     --ink-muted and went solid under the pointer, which is the right gesture
     for a control that is one of forty and the wrong one for the eight things
     in a menu you opened deliberately — a whole panel of muted grey reads as a
     panel of disabled options. The picker and the palette result are both
     solid ink at rest and answer the pointer with the tint alone; this now
     matches them. --ink-muted stays, and does one job: the quiet rows. */
  .menu__item:hover { background: var(--surface-hover); }

  /* The row icons.

     20px. The status marks are 18px and the paw on the button is 22px, so the
     product now has three icon sizes where it used to claim one — worth saying
     rather than leaving to be discovered. Each is measured against what it
     sits beside: a mark inside a 64px row, a glyph on a 64px button, and these
     against a menu row's 19px label, where 18px read as an afterthought beside
     the words and 22px crowded them.

     stroke rather than fill and currentColor rather than a token, so an icon
     is the same colour as the words beside it in all three states a row has —
     resting, hovered, and the one you are already on.

     flex: none for the same reason the paw needs it: an SVG with no intrinsic
     width in a flex row will let the label squeeze it.

     A row without an icon still lines up, because the title is what the gap is
     measured from and every row in a menu that uses icons has one. The team
     menu has none and is unaffected. */
  .menu__icon {
    width: 1.25rem;
    height: 1.25rem;
    flex: none;
    opacity: 0.75;
  }

  /* Where you already are: the icon goes as solid as the label does. */
  .menu__item[aria-current] .menu__icon,
  .menu__item:hover .menu__icon { opacity: 1; }

  /* Where you already are: the view you are reading, or the status this task
     is already on. Both are the same fact and read the same way. */
  .menu__item[aria-current] {
    background: var(--accent-sunk);
    color: var(--accent);
    font-weight: var(--weight-bold);
  }

  /* The rows below the last rule: sign out, switch workspace, the trash. Quiet
     by tint and by position, and by nothing else — separation is space and tint
     before it is anything else, and shrinking these to --text-sm was a second
     answer to a question the rule above them had already answered.

     The hover is stated here because it is no longer stated anywhere else: the
     shared .menu__item:hover used to carry a colour and now carries only the
     tint, since the ordinary rows are solid ink already. These are the one kind
     of row that still has somewhere to go under the pointer. */
  .menu__item--quiet { color: var(--ink-muted); }
  .menu__item--quiet:hover { color: var(--ink); }

  /* A form wrapper around a menu item must not break the column.

     A <button> sizes itself to its own text however it is displayed — that is
     form-control sizing, and `display: flex` on .menu__item does not change
     it. What makes the rows line up is being a flex item of the column, where
     align-items: stretch takes over; the form in between has to get out of the
     way for that to happen, which is what display: contents is for.

     A descendant selector and not `.menu > form`, which is what this was. That
     matched while every menu was one panel and the form was a direct child of
     the popover, and stopped matching the day the status menu grew a
     .menu__frame around its list — one level deeper, no longer a child, no
     rule. The form went back to being a block, the buttons went back to
     sizing themselves, and "New" drew at 128px of a 240px menu while "In
     progress" drew at 193px. Nothing said so; a selector that stops matching
     is silent.

     So it is written to survive the next panel: any form inside a menu, at any
     depth, gets out of the way of the column it is in. */
  .menu form { display: contents; }
  .menu form > .menu__item { width: 100%; }

  .menu__title { margin-right: auto; }


  .menu__count {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--ink-faint);
  }

  .menu__item[aria-current] .menu__count { color: inherit; }

  /* The other way a row says there is something waiting, where a number is not
     a question anybody asks — the changelog is one page, not a pile. It
     is the same dot as the one on the masthead's face, without the ring: that
     one is cut out of the avatar behind it and this one has nothing to cut
     itself out of. */
  .menu__dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-pill);
    background: var(--accent);
  }

  /* No keyboard, no keyboard hint — the row still works, it just stops
     advertising a shortcut the device cannot press. */
  @media (hover: none) {
    .menu__count--key { display: none; }
  }

  /* Set as a label in base.css; only the room it takes in the menu is here. */
  .menu__label { padding: var(--space) var(--space-2) calc(var(--space) / 2); }

  /* A sentence in a menu, which happens once: the line under Favourites while
     there is nothing under Favourites. It is not a row — there is
     nothing to press and nothing to arrow onto — so it takes neither the row's
     height nor its ink, and it wraps, because it is the one thing in here that
     is a sentence rather than a name. --text-sm is the exception the note
     above .menu__item allows itself: this is the system talking about the
     list, like the label above it, and it is not offering a choice. */
  .menu__aside {
    padding: 0 var(--space-2) var(--space);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    color: var(--ink-faint);
    max-width: none;
  }

  .menu__rule {
    height: 1px;
    background: var(--line);
    margin: var(--space) calc(var(--space) * -1);
  }

  /* Anchored to the bottom-left cluster, measured from the taller of the two
     buttons in it. --pounce-h rather than --control-h: the Pounce button sets
     the height of that row, and the arithmetic has to follow whichever button
     is tallest or the menu opens across it. */
  .menu--above {
    inset: auto auto
           calc(var(--pounce-h) + var(--space-4) + var(--safe-bottom))
           calc(var(--space-3) + var(--safe-left));

    /* And what the cluster leaves it. This one grows upwards, so without a cap
       a long list of colleagues in the lens menu ran off the top of the window
       instead of the bottom — the same bug, drawn the other way up. */
    max-height: calc(100dvh - var(--pounce-h) - var(--space-4) - var(--safe-bottom) - var(--space-3));
  }

  /* Anchored under the masthead, at the same edge as its content. A popover is
     in the top layer and can be positioned against the viewport and nothing
     else, so both offsets are arithmetic off --masthead-h and --gutter rather
     than a guess — no anchor positioning needed, which keeps this working
     outside Chromium.

     It was a guess: 5rem and --space-4, both hard-coded. On a phone the bar
     wraps to two rows and grows to 8rem, and the menu opened 48px up inside it,
     covering the capture field it is supposed to sit under. Both numbers now
     come from the same tokens the masthead itself is built from, so the two
     cannot drift apart again. */
  .menu--under-masthead {
    inset: calc(var(--masthead-h) + var(--safe-top) + var(--space)) auto auto var(--menu-edge);
  }

  .menu--under-masthead-right {
    inset: calc(var(--masthead-h) + var(--safe-top) + var(--space)) var(--menu-edge) auto auto;
  }

  /* What the masthead leaves them, off the same arithmetic that puts them
     there — the bar, the notch, the gap under it, and a gap at the foot so the
     menu ends before the window does rather than against it. */
  .menu--under-masthead,
  .menu--under-masthead-right {
    max-height: calc(100dvh - var(--masthead-h) - var(--safe-top) - var(--space) - var(--space-3));
  }

  /* The masthead's content edge: the gutter, or wherever the centred column
     puts it once the window is wider than the column. */
  .menu {
    --menu-edge: max(var(--gutter), calc((100vw - var(--column)) / 2 + var(--gutter)));
  }
}
