/* The one modal behind shared/_dialog, and the ⋯ action dialog. Both float,
   so both take the float radius and the single warm shadow. */
@layer components {
  /* Any modal holds the page still behind it — a form, the ⋯ dialog, the
     lightbox. :modal is showModal's own state, so the hold ends with the
     dialog. `hidden`, not `clip`: the page's scroller is the root, where
     Chrome ignores clip (layout.css). Hidden took the root's scrollbar
     track away, and the centred sheet moved by its width as any modal
     opened (owner, 2026-10-20): the stable gutter keeps the track's room —
     measured, the sheet stays where it stood. */
  html:has(dialog:modal) {
    overflow: hidden;
    scrollbar-gutter: stable;
  }

  /* The one modal, behind shared/_dialog for every form in the app. Sized by
     its content, never taller than the viewport. It floats, so it takes the
     float radius and the single warm shadow instead of the flat-surface
     radius that cards and inputs use. */
  .modal {
    /* The two a variant retunes. Everything else about a modal — the float
       radius, the warm shadow, the scrim, the 85dvh ceiling — is the same
       whatever is inside it, so a wider one sets these and nothing else.
       Height is `fit-content` for every modal but one: a form is as tall as
       it is. NOT `auto` — a modal dialog sits in the top layer with the UA's
       `inset: 0`, where auto means STRETCH, and every form dialog quietly
       filled to the 85dvh ceiling with a void under its buttons. fit-content
       is the UA's own dialog sizing, restated because this declaration
       exists to be retuned. */
    --modal-inline-size: 34rem;
    --modal-block-size: fit-content;

    inline-size: min(var(--modal-inline-size), 92vw);
    block-size: var(--modal-block-size);
    /* A fresh surface, like the action dialog: a modal renders wherever its
       trigger lives, and the intake's add-card dialog inherited the tool
       card's centering — every label and input drifted to the middle. */
    text-align: start;
    max-block-size: 85dvh;
    margin: auto;
    padding: var(--block-space-double) var(--inline-space-double);
    overflow-y: auto;
    color: var(--color-ink);
    background-color: var(--color-canvas);
    border: var(--border);
    border-radius: var(--border-radius-float);
    box-shadow: var(--shadow);

    /* The fade the starting style always meant: without a transition the
       rule was inert and every modal popped (found porting the lightbox,
       2026-08-24). Both ways — the scrim breathes with the card — on the
       quick clock (HANSHI). */
    opacity: 1;
    transition:
      display var(--speed-quick) allow-discrete,
      overlay var(--speed-quick) allow-discrete,
      opacity var(--speed-quick) var(--ease-out);

    @starting-style {
      opacity: 0;
    }

    &:not([open]) {
      opacity: 0;
    }

    &::backdrop {
      background-color: var(--color-scrim);
      transition:
        display var(--speed-quick) allow-discrete,
        overlay var(--speed-quick) allow-discrete,
        background-color var(--speed-quick) var(--ease-out);

      @starting-style {
        background-color: transparent;
      }
    }
  }

  /* Settings is the one modal whose content is a whole layout rather than a
     form, so it is the one that says all four of these.

     ONE HEIGHT, whatever pane you pick. Panes differ by a lot — four rail rows
     against a session per browser — and a dialog that resizes under the cursor
     between two clicks is the thing you notice (user, 2026-08-13). Fixed here,
     still capped at 85dvh above, and the pane scrolls inside it: which is why
     the modal's own overflow is shut off. The padding goes with it — the rail
     is tinted to the dialog's own edges, and the shell puts the room back
     inside each column (settings.css). */
  .modal--settings {
    --modal-inline-size: 56rem;
    --modal-block-size: 36rem;

    padding: 0;
    overflow: hidden;

    /* A phone has height to spare and no width: the dialog takes the whole
       ceiling, so the pane under the tabs is a page and not a slot. */
    @media (width < 35rem) {
      --modal-block-size: 85dvh;
    }
  }

  /* The card dialog: a description editor wants more line than the default
     form column gives it (user, 2026-08-14 — "a tad cramped"; again
     2026-10-06, so a little more). */
  .modal--wide {
    --modal-inline-size: 46rem;
  }

  /* A composer in a dialog is the dialog's longest field, and at one line it
     sat on the buttons (user, 2026-10-06). Three lines, everywhere: it used to
     be four on a phone only, written when lexxy's own floor was eight and the
     phone was the one giving room back. Still a floor and the same focused or
     not, so the click moves nothing (lexxy.css). */
  .modal .composer lexxy-editor {
    --lexxy-editor-rows: 3lh;
  }

  /* Hierarchy by weight and the space under it, not by size — the title sits
     one step above body text and lets the whitespace do the separating. */
  .modal__title {
    font-size: var(--text-medium);
    font-weight: 600;
    margin-block-end: var(--block-space);
  }

  /* A form's buttons already say how to leave; only a modal that holds a
     chooser needs telling. Same words, same whisper as the action dialog. */
  .modal__dismiss {
    margin-block-start: var(--block-space);
    font-size: var(--text-x-small);
    color: var(--color-ink-faint);
  }

  .modal:has(.actions) .modal__dismiss {
    display: none;
  }

  /* And on a keyboardless screen there is no esc to speak of (user,
     2026-08-24) — the popups' footers stand down the same way (popups.css).
     The house touch query (AGENTS.md): `hover: none` asks only about the
     primary pointer. */
  @media (any-hover: none) {
    .modal__dismiss {
      display: none;
    }
  }

  /* Everything else a card or record can do lives behind ⋯ in this one spare
     dialog: context line on top, plain rows, destructive last after a
     hairline, "esc to close" underneath. It floats — float radius and the warm
     shadow.

     It is a .popup — see popups.css for why a menu is a popover and never a
     <dialog>, and for everything it inherits from being one. All this adds is
     where it hangs: off the ⋯'s own end corner rather than centred under it,
     which is the difference between a menu and a palette. */
  .action-dialog {
    --popup-area: block-end span-inline-start;

    min-inline-size: 16rem;

    /* A menu is a fresh surface, not a continuation of whatever it hangs off.
       The day divider whispers in tracked uppercase, and the person card
       opening from its faces inherited the shouting — a name is a name. */
    text-transform: none;
    letter-spacing: normal;

    /* The menus that open upward, start edges aligned — the ⋯ area
       mirrored: the one at the bottom of the screen, up off the avatar, and
       "Comment", above the words picked (annotate_controller) and under them
       by the popup's own flip where there is no room above. Here and not in
       layout.css because this assignment must sit in the same layer to win. */
    #footer &,
    &.annotate-menu {
      --popup-area: block-start span-inline-end;

      margin-block-end: var(--block-space-half);
    }

    /* "Comment" is one row: as narrow as it. */
    &.annotate-menu {
      min-inline-size: auto;
    }

    /* And the one that hangs in a left gutter — the to-do row's ⋯ — opens
       toward the text: centring over the margin would run it off the sheet. */
    .todo__menu & {
      --popup-area: block-end span-inline-end;
    }

    /* On a narrow shell that same ⋯ stands at the row's END (todos.css), so
       the menu mirrors with it — toward the text again, now leftward. Without
       this it kept opening rightward, straight off a phone's edge
       (user, 2026-08-11). */
    @media (width < 35rem) {
      .todo__menu & {
        --popup-area: block-end span-inline-start;
      }
    }

    header {
      padding: var(--block-space-half) var(--inline-space);
      font-size: var(--text-x-small);
      color: var(--color-ink-subtle);
    }

    menu {
      list-style: none;
      margin: 0;
      padding: 0;
    }

    /* A row is a link or a button_to depending on the verb — or a label
       around a file input, for "Upload files…"; all read the
       same. Scoped to the dialog, so no control rule escapes into the page. */
    menu :where(a, button, label) {
      /* A flex row rather than a block, so a row that leads with a glyph
         (the chat line's menu) lines it up with the word. */
      display: flex;
      align-items: center;
      gap: var(--inline-space);
      inline-size: 100%;
      padding: var(--block-space-half) var(--inline-space);
      font-weight: 400;
      text-align: start;
      color: var(--color-ink);
      background-color: transparent;
      border: none;
      border-radius: var(--border-radius);
      text-decoration: none;
      cursor: pointer;

      &:hover {
        background-color: var(--color-surface);
      }
    }

    /* A row that is a fact rather than a verb — "1 person was notified" —
       wears the row's shape in the subtle ink and answers no pointer. */
    menu .action-dialog__note {
      display: block;
      padding: var(--block-space-half) var(--inline-space);
      color: var(--color-ink-subtle);
    }

    /* The destructive row is one of the four places shu is spent; on its own
       tint it takes the paired ink, as every tint does. */
    menu .action-dialog__row--destructive {
      color: var(--color-accent);

      &:hover {
        color: var(--color-accent-ink);
        background-color: var(--color-accent-tint);
      }
    }

    footer {
      margin-block-start: var(--block-space-half);
      padding: var(--block-space-half) var(--inline-space) 0;
      border-block-start: var(--border);
      font-size: var(--text-x-small);
      color: var(--color-ink-faint);
    }
  }

  /* Destructive last, after a hairline — and a picker's last door after the
     same one (pickers.css), listed here as labels.css lists foreign
     selectors. */
  .action-dialog menu li.action-dialog__row--separated,
  .picker li.picker__row--separated {
    border-block-start: var(--border);
    margin-block-start: var(--block-space-half);
    padding-block-start: var(--block-space-half);
  }
}
