@layer cinder.tokens, cinder.foundation, cinder.components, cinder.utilities;
@layer cinder.components {
  /* ========================================
 * MODAL
 * ======================================== */

  .cinder-modal {
    /* Reset browser's default <dialog> styles */
    margin: auto;
    padding: 0;
    border: none;
    border-radius: var(--cinder-radius-lg);
    background: transparent;
    color: var(--cinder-text-default);
    max-width: min(90vw, 32rem);
    width: 100%;

    /* Keep dialog-owned anchored surfaces in the native top layer visible.
     * The panel below remains the clipping boundary for ordinary content. */
    max-height: calc(100dvh - var(--cinder-space-8));
    overflow: visible;
  }

  /* Chromeless / full-bleed mode (`chrome="none"`): suppresses max-width so the
   * dialog fills the viewport. The header, border, and body padding are
   * suppressed further down, scoped to `.cinder-modal__panel[data-cinder-chrome='none']`
   * and `.cinder-modal__body[data-cinder-chrome='none']`. All coordination logic
   * (focus trap, scroll lock, escape stack, exit-transition lifecycle) is
   * untouched — this block is presentation only. */
  .cinder-modal[data-cinder-chrome='none'] {
    max-width: none;
    width: 100%;
    height: 100%;
    max-height: 100dvh;
    inset: 0;
    margin: 0;
  }

  /* Backdrop — native <dialog>::backdrop is controlled by the UA.
   * `::backdrop` does NOT reliably inherit custom properties from its
   * originating element across engines (some engines inherit from the
   * dialog, some effectively from the initial containing block/`:root`,
   * some not at all).
   *
   * The fallback lives on the CONSUMING property (`background-color`
   * below), NOT on a redeclaration of `--cinder-modal-backdrop` itself.
   * `--cinder-modal-backdrop: var(--cinder-modal-backdrop, fallback)` looks
   * like a reasonable "use the inherited value, or fall back" pattern, but
   * it is a CSS custom-property dependency CYCLE — a property referencing
   * itself, even guarded by a fallback — which the spec resolves by making
   * the property invalid at computed-value time (cycle detection happens
   * BEFORE fallback substitution). That breaks the backdrop for every Modal
   * with no override at all, which is strictly worse than the reliability
   * problem this rule exists to solve.
   *
   * Declaring NOTHING here for `--cinder-modal-backdrop` and only consuming
   * it via `var(--cinder-modal-backdrop, var(--cinder-overlay-backdrop))`
   * is what's actually correct: whatever value the property resolves to for
   * `::backdrop`'s own scope (inherited from `:root`/an ancestor in
   * whichever engines route inheritance that way, or from a same-selector
   * `::backdrop` override, or simply unset) is used as-is, falling back to
   * `--cinder-overlay-backdrop` only when the property is truly unset/
   * invalid here. A consumer override must still target `::backdrop`
   * directly for cross-engine reliability — see `ImageLightbox`'s
   * `:global(.lightbox-modal::backdrop)` rule for the pattern; a
   * class-scoped override on the element alone is not reliably visible
   * here. */
  .cinder-modal::backdrop {
    background-color: var(--cinder-modal-backdrop, var(--cinder-overlay-backdrop));
    backdrop-filter: blur(var(--cinder-overlay-blur));
    transition:
      background-color var(--cinder-duration-normal) var(--cinder-ease-standard),
      backdrop-filter var(--cinder-duration-normal) var(--cinder-ease-standard);
    /* `::backdrop` is added to/removed from the top layer as a discrete step
     (like `display`), so `@starting-style` below needs `allow-discrete` to
     actually engage on insertion instead of silently no-op'ing. */
    transition-behavior: allow-discrete;
  }

  .cinder-modal[data-cinder-closing]::backdrop {
    background-color: transparent;
    backdrop-filter: blur(0);
  }

  @starting-style {
    .cinder-modal::backdrop {
      background-color: transparent;
      backdrop-filter: blur(0);
    }
  }

  /* ----------------------------------------
 * Panel (the visible card inside the dialog)
 * ---------------------------------------- */

  .cinder-modal__panel {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--cinder-surface-raised);
    border: 1px solid var(--cinder-border);
    border-radius: var(--cinder-radius-lg);
    box-shadow: var(--cinder-shadow-lg);
    max-height: inherit;
    overflow: hidden;
    opacity: 1;
    /* No resting `translate` here (unlike the `[data-cinder-closing]`/
     `@starting-style` states below) — ANY non-`none` transform/translate
     value, including a no-op `0 0`, establishes a CSS containing block,
     which would silently break `position: fixed` for every descendant
     (e.g. a portal-owned popover surface) for the entire time the modal is
     open, not just mid-transition. `none` (the default) transitions to/from
     a real value just fine — matching Drawer/Sheet, which never set a
     resting translate either. */
    transition:
      opacity var(--cinder-duration-normal) var(--cinder-ease-standard),
      translate var(--cinder-duration-normal) var(--cinder-ease-spring);
  }

  /* Exit transition: the component keeps the panel mounted for the duration
   of this transition (via the shared `SlidingDialogState`/`data-cinder-closing`
   lifecycle Drawer and Sheet also use) instead of removing it the instant
   `open` goes false, so this can actually play. */
  .cinder-modal__panel[data-cinder-closing] {
    opacity: 0;
    translate: 0 -0.5rem;
    pointer-events: none;
  }

  @starting-style {
    .cinder-modal__panel {
      opacity: 0;
      translate: 0 -0.5rem;
    }
  }

  /* Chromeless / full-bleed mode: the panel fills the chromeless dialog
   * and drops the card chrome (border, radius, surface, shadow) so the
   * consumer's own content (e.g. an image lightbox) owns the full surface. */
  .cinder-modal__panel[data-cinder-chrome='none'] {
    height: 100%;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
  }

  /* ----------------------------------------
 * Header
 * ---------------------------------------- */

  .cinder-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cinder-space-4);
    /* Reserve trailing inline space so the title doesn't run under the
     absolute-positioned close button. */
    padding-block: var(--cinder-space-4);
    padding-inline-start: var(--cinder-space-5);
    padding-inline-end: calc(var(--cinder-space-5) + 2.25rem);
    border-block-end: 1px solid var(--cinder-border-muted);
    background: var(--cinder-surface);
    flex-shrink: 0;
  }

  /* With no close button to keep clear of, a header that carries trailing content uses the same
     padding at both edges. */
  .cinder-modal__header[data-cinder-trailing]:not([data-cinder-closable]) {
    padding-inline-end: var(--cinder-space-5);
  }

  /* Content opposite the title (a count) reads on the title's line, muted and out of its way. */
  .cinder-modal__header-trailing {
    flex: none;
    font-size: var(--cinder-text-sm);
    color: var(--cinder-text-muted);
  }

  /* The title with the line that says what the dialog is for under it. */
  .cinder-modal__heading {
    display: flex;
    flex-direction: column;
    gap: var(--cinder-space-1);
    min-inline-size: 0;
  }

  .cinder-modal__description {
    margin: 0;
    font-size: var(--cinder-text-sm);
    line-height: var(--cinder-leading-snug);
    color: var(--cinder-text-muted);
  }

  .cinder-modal__title {
    margin: 0;
    font-size: var(--cinder-text-lg);
    font-weight: var(--cinder-font-semibold);
    line-height: var(--cinder-leading-snug);
    color: var(--cinder-text-default);
  }

  /* ----------------------------------------
 * Close button
 * ---------------------------------------- */

  .cinder-modal__close {
    /* Rendered last in DOM order so the close button isn't the first focusable
     element on dialog open — but visually positioned in the header corner. */
    position: absolute;
    inset-block-start: var(--cinder-space-3);
    inset-inline-end: var(--cinder-space-3);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    /* The close control is a 36px icon button (COR-2322 AC5). */
    width: 2.25rem;
    height: 2.25rem;
    padding: var(--cinder-space-1);

    border-radius: var(--cinder-radius-md);
    border: none;
    background: transparent;
    color: var(--cinder-text-muted);
    cursor: pointer;

    transition:
      background-color var(--cinder-duration-fast) var(--cinder-ease-standard),
      color var(--cinder-duration-fast) var(--cinder-ease-standard),
      box-shadow var(--cinder-duration-fast) var(--cinder-ease-standard);
  }

  @media (hover: hover) {
    .cinder-modal__close:hover {
      background: color-mix(in oklch, currentColor, transparent 85%);
      color: var(--cinder-text-default);
    }
  }

  .cinder-modal__close:focus-visible {
    /* The close button sits in the panel corner, and the panel has
     `overflow: hidden`. An outset box-shadow ring would be clipped there, so
     paint an INSET ring (Strategy B-inset) entirely within the button's own
     border box. The transparent outline placeholder reserves the channel the
     forced-colors fallback repaints (WCAG 2.4.7). */
    outline: var(--cinder-ring-width) solid transparent;
    box-shadow: inset 0 0 0 var(--cinder-ring-width)
      var(--_cinder-modal-close-ring, var(--cinder-ring-color));
  }

  @media (forced-colors: active) {
    .cinder-modal__close:focus-visible {
      outline: var(--cinder-ring-width) solid ButtonText;
      outline-offset: calc(var(--cinder-ring-width) * -1);
    }
  }

  .cinder-modal__close-icon {
    width: 1.25rem;
    height: 1.25rem;
  }

  /* ----------------------------------------
 * Body
 * ---------------------------------------- */

  .cinder-modal__body {
    --_cinder-scroll-fade-color: var(--cinder-surface);

    flex: 1;
    overflow-y: auto;
    /* The header, body and footer share one inline inset (space-5), so the title, the body text
       and the footer actions start on one edge (s4#8). No gutter is reserved: a reserved gutter
       made the body's inline-end inset wider than its start whenever the scrollbar is classic,
       even when nothing scrolls. */
    padding: var(--cinder-space-6) var(--cinder-space-5);
    background: var(--cinder-surface);
    overscroll-behavior: contain;
    scrollbar-gutter: auto;
    /* The body container is focused on open (tabindex=-1) when the dialog has
     no autofocus target. The ring is hidden because focus arrived
     programmatically — pressing Tab forward will move into real content. */
    outline: none;
  }

  .cinder-modal__body[data-cinder-chrome='none'] {
    padding: 0;
    background: transparent;
  }

  /* The shared `.cinder-_scroll-fade` recipe (see `_scroll-fade.css`) fades
   * with an OPAQUE overlay painted in `--_cinder-scroll-fade-color`
   * (`--cinder-surface` here) by design — masking a container that paints
   * its own background would make the fade light up instead of fading out
   * over a brighter parent surface (see that file's design-rule #1). A
   * chromeless body is transparent/full-bleed on purpose (an image
   * lightbox's photo, for instance) with no surface color of its own to
   * fade INTO, so that same opaque band is simply wrong here — it would
   * paint a solid `--cinder-surface` stripe across arbitrary full-bleed
   * content. `content: none` fully suppresses the generated `::after` box
   * (rather than merely zeroing `opacity`, which a running
   * `animation-timeline: scroll()` keyframe would still override — see this
   * file's own forced-colors comments for why `opacity` alone can't win
   * against that animation), so no fade box exists at all in the chromeless
   * chrome. */
  .cinder-modal__body[data-cinder-chrome='none']::after {
    content: none;
  }

  /* ----------------------------------------
 * Footer
 * ---------------------------------------- */

  .cinder-modal__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--cinder-space-3);
    padding: var(--cinder-space-3) var(--cinder-space-5);
    border-block-start: 1px solid var(--cinder-border-muted);
    background: var(--cinder-surface);
    flex-shrink: 0;
  }

  /* Chromeless mode fills the dialog's entire content box with the panel/
   * body/footer as one full-bleed surface — the base rule's card-style
   * background, top border, and padding would paint a visible seam between
   * the footer and a consumer's own full-bleed content, breaking the
   * "genuinely full-bleed" contract chrome="none" promises. Reset all three,
   * matching the identical pattern already applied to
   * `.cinder-modal__body[data-cinder-chrome='none']` above. */
  .cinder-modal__footer[data-cinder-chrome='none'] {
    padding: 0;
    background: transparent;
    border-block-start: none;
  }

  /* ----------------------------------------
 * Reduced motion
 * ---------------------------------------- */

  @media (prefers-reduced-motion: reduce) {
    :root:not([data-cinder-reduced-motion='false']):not([data-reduced-motion='off'])
      .cinder-modal__panel {
      transition: none;
    }

    :root:not([data-cinder-reduced-motion='false']):not([data-reduced-motion='off'])
      .cinder-modal::backdrop {
      transition: none;
    }
  }

  /* ----------------------------------------
 * Bottom sheet (row 05d)
 *
 * Below `sheetBelow` the modal docks to the bottom edge, as a drawer placed at the bottom does:
 * full width, rounded top corners, a grabber, a height capped so the body scrolls inside the sheet
 * instead of the sheet outgrowing the screen, and a lift above the on-screen keyboard
 * (`--_cinder-keyboard-inset`, published while it is open). The footer's actions stack at full
 * width and 44px with the last one, the confirmation, on top; DOM order is unchanged, so the
 * first action keeps initial focus. Physical properties, as in drawer.css, because the sheet is
 * physically bottom anchored in every writing mode.
 * ---------------------------------------- */

  .cinder-modal[data-cinder-sheet='docked'] {
    inset-block-start: auto;
    inset-block-end: var(--_cinder-keyboard-inset, 0px);
    inset-inline: 0;
    margin: 0;
    max-width: none;
    max-height: calc(90dvh - var(--_cinder-keyboard-inset, 0px));
    border-radius: 0;
  }

  /* The sheet's shape (COR-2322 AC4): a 20px top radius, a square bottom edge that meets the
   * screen, an upward shadow, and 32px of padding under the last row (more where the device
   * reports a bottom safe area). The panel paints the sheet surface so the padding band reads as
   * part of the sheet rather than a raised strip under the footer. Black, not a theme colour,
   * because a shadow is darkness, not ink: it has to darken the page in both themes. */
  .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__panel {
    border-width: 1px 0 0;
    border-radius: 1.25rem 1.25rem 0 0;
    background: var(--cinder-surface);
    box-shadow: 0 -0.5rem 1.5rem -0.25rem color-mix(in oklch, black, transparent 80%);
    padding-block-end: max(var(--cinder-space-8), env(safe-area-inset-bottom));
  }

  .cinder-modal__grabber {
    flex-shrink: 0;
    inline-size: 2.25rem;
    block-size: 0.25rem;
    margin: var(--cinder-space-2) auto var(--cinder-space-1);
    border-radius: var(--cinder-radius-full);
    background: var(--cinder-border-contrast);
  }

  .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__footer {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__footer > * {
    inline-size: 100%;
  }

  /* Footer buttons and the body's fields are full 44px targets in the sheet. The selector repeats the button's own
   * attribute so it outranks the compact height without a cross-component custom property. */
  .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__footer .cinder-button[data-cinder-size],
  .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__body .cinder-input.cinder-input {
    min-height: var(--cinder-touch-target-min);
  }

  .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__panel[data-cinder-closing] {
    translate: 0 100%;
  }

  @starting-style {
    .cinder-modal[data-cinder-sheet='docked'] .cinder-modal__panel {
      translate: 0 100%;
    }
  }

  /* Reduced motion: no slide, a cross-fade instead (design row 05d). foundation.css forces every
   * CSS transition to 0.01ms under reduced motion, so the fade is played from script
   * (reduced-motion-fade.ts); CSS only keeps the panel where it is. */
  @media (prefers-reduced-motion: reduce) {
    :root:not([data-cinder-reduced-motion='false']):not([data-reduced-motion='off'])
      .cinder-modal[data-cinder-sheet='docked']
      .cinder-modal__panel[data-cinder-closing] {
      translate: none;
    }
  }

  :root[data-reduced-motion='on']
    .cinder-modal[data-cinder-sheet='docked']
    .cinder-modal__panel[data-cinder-closing] {
    translate: none;
  }
}
