@use '@firestitch/style/styles' as fs-style;
@use '@firestitch/style/variables' as fs-variables;

/*
  THE COLUMN HAS TO REACH THE BUTTONS' OWN PARENT. Ray 2026-09-15 ("it's not
  stacking the buttons ever now").

  This set flex-direction on mat-dialog-actions alone, and that element does
  not hold the buttons. A footer built with fs-form-dialog-actions nests three
  deep:

    mat-dialog-actions          <- what this used to stack
      fs-form-dialog-actions    <- :host { display: flex } - the real row
        button.mdc-button

  Turning mat-dialog-actions into a column reflows its ONE child, so nothing
  moved. Meanwhile full-width-action-appearance() matches
  `mat-dialog-actions .mdc-button` - a DESCENDANT selector, which crosses the
  extra level without caring - so the buttons took the filled and outlined
  treatment while staying side by side. Appearance applied, layout did not, and
  that split is the symptom this comment exists to explain.

  So both elements are stacked: whichever of them is the buttons' parent, that
  parent is a column. mat-dialog-actions stays in the list because a dialog
  that writes its own <button>s directly - no fs-form-dialog-actions - has its
  buttons at that level.

  !IMPORTANT IS REQUIRED ON THE HOST, and not for specificity. The display:flex
  lives in fs-form-dialog-actions' own emulated-encapsulation styles, which are
  emitted with an _nghost attribute this stylesheet cannot name; without
  !important the component's own rule wins however the selector here is
  written. Same reason the width on the buttons carries it - MatButton sets its
  own.
*/
@mixin full-width-actions() {
  mat-dialog-actions,
  fs-form-dialog-actions {
    flex-direction: column !important;
    flex-wrap: nowrap;
    display: flex !important;
    align-items: stretch !important;
    flex: none;
    min-height: auto;

    .mdc-button {
      width: 100% !important;
      margin: 0 0 4px 0;

      &:last-child {
        margin-bottom: 0;
      }
    }
  }

  /*
    The nested host is the row itself, so it must not also be a full-width
    "button" in its parent column - it is the container of them.
  */
  mat-dialog-actions > fs-form-dialog-actions {
    width: 100%;
  }
}

/*
  THE APPEARANCE A FULL-WIDTH ACTION WEARS - Ray 2026-09-14.

  Paired with full-width-actions() above, which stacks the row and gives every
  button the full width. At that size the appearances have to change too: a text
  button spanning the screen reads as a label rather than a target, so the
  primary action takes a FILL and everything beside it takes an OUTLINE.

  In a FLOATING dialog none of this applies - every button stays text, with
  emphasis carried by colour alone. That is Material 3's dialog guidance (text
  actions, at most one filled button anywhere) and it is why this lives in a
  mixin applied only at the mobile breakpoint rather than on the actions row
  unconditionally.

  CSS, NOT setAppearance(). MatButton.setAppearance() exists only on Material 20,
  this package builds against 18, and there is no supported way to reach a
  button instance from a directive here - getComponent() is not a named export of
  @angular/core, and a @ContentChildren(MatButton) query cannot cross into
  fs-form-dialog-actions' own template, which is where Save / Cancel / Close
  actually live. Styling the rendered classes has none of those problems and
  works identically on both majors.

  THE ROLE IS `.mat-primary`, the class Material puts on a button host when the
  template sets color="primary" - the same hook the M2 theme itself keys off
  (`:root .mat-mdc-button.mat-primary`). The footer standard is exactly one
  color="primary" per dialog, so no template names an appearance and a package
  rendering its own footer follows along without being rebuilt.

  EVERY COLOUR IS STATED, none inherited. An app on an M2 theme defines no
  --mat-sys-* tokens, so Material's own `var(--mat-button-filled-container-color,
  var(--mat-sys-primary))` fallbacks resolve to nothing. The --fs-* names are the
  shared contract from @firestitch/style, which flips them per scheme at :root,
  so both schemes follow with no second block here. The fallbacks are the LIGHT
  half, for a host with no style package at all -- which has a white page.
*/
@mixin full-width-action-appearance() {
  mat-dialog-actions {
    .mdc-button {
      /* The outlined treatment, which every non-primary action takes. */
      border: var(--mat-button-outlined-outline-width, 1px) solid
        var(--fs-hairline-strong, rgba(0, 0, 0, 0.38));
      border-radius: var(--mat-button-outlined-container-shape, 9999px);
      background-color: transparent;
      color: var(--fs-color-text, rgba(0, 0, 0, 0.87));

      /* The one filled action. --fs-color-primary is the app's own brand,
         configured through @firestitch/style's $primary-color knob; the
         fallback tracks that same knob rather than repeating its default, so an
         app that configured a brand never sees this package contradict it. */
      &.mat-primary {
        border-color: var(--fs-color-primary, #{fs-variables.$primary-color});
        background-color: var(--fs-color-primary, #{fs-variables.$primary-color});
        color: var(--fs-surface-background, #FFFFFF);
      }
    }
  }
}

.cdk-overlay-pane {
  &.fs-dialog-overlay-pane {
    &.fs-dialog-mode-stretch {
      @media (min-width: fs-style.$break-xsmall) {
        max-width: none;
        max-height: none;
        width: calc(100% - 50px) !important;
        height: calc(100% - 50px) !important;
      }

      .mat-mdc-dialog-content {
        max-height: none !important;
        flex: 1;
      }
      
      fs-dialog {
        height: 100%;
        display: flex;
        flex-direction: column;
      }
      
      form {
        height: 100%;
      }
  
    }
    
    /*
      FULL MODE IS THE SCREEN, NOT A PANEL INSIDE IT.

      The dialog runs edge to edge, under the status bar and the home
      indicator, and the safe areas are padding INSIDE it. They were
      transparent borders at 80% of the inset, so the backdrop showed as a band
      above and below the dialog, with its shadow and corners cut against it.

      The padding sits on the container, not the surface, because the surface
      is the scroller: padding there scrolls off with the title and lets the
      content ride up under the clock. The container stays put, so it is
      painted with Material's own chain for the surface colour (v20 token, then
      v18) and the band cannot drift from what the surface paints.
    */
    &.fs-dialog-mode-full {
      $surface: var(--mat-dialog-container-color, var(--mdc-dialog-container-color, var(--mat-sys-surface, white)));

      .mat-mdc-dialog-container {
        padding-top: fs-style.$safe-area-top;
        padding-bottom: fs-style.$safe-area-bottom;
        background-color: $surface;
      }

      .mat-mdc-dialog-surface {
        border-radius: 0;
        box-shadow: none;
      }

      /*
        ONE BUTTON PLACEMENT, DECIDED HERE AND NOT BY THE TEMPLATE'S WRAPPER.

        fs-dialog was a height: 100% flex column and Material grows
        .mat-mdc-dialog-content, so with nothing around fs-dialog the content
        stretched and the actions pinned to the bottom of the screen. Inside a
        <form> the percentage had nothing definite to resolve against and the
        same dialog flowed its actions straight under the content. Which one a
        dialog got came down to whether its template happened to have a form.

        The top-level form drops out of the box tree, so fs-dialog is always a
        direct flex item of the surface and mobileButtonPlacement alone decides:

          flow   - the actions follow the content. Once the content is taller
                   than the screen they stick to its bottom edge rather than
                   scroll out of reach, so a long form never hides its Save.
          bottom - the actions sit at the foot of the screen even when the
                   content is short.
      */
      .mat-mdc-dialog-component-host > form {
        display: contents;
      }

      fs-dialog {
        display: block;
        flex: 0 0 auto;
      }

      /*
        THE ACTIONS FLOW UNDER THE CONTENT - THEY ARE NOT PINNED. Ray 2026-09-14.

        They used to be `position: sticky; bottom: 0`, which pinned them to the
        floor of the screen whatever the content did. On a dialog whose content
        fills a quarter of the phone - an informational list with a single Close
        - that left the button a thousand pixels below the last row, detached
        from the thing it belonged to and easy to miss entirely.

        Pinning solves a real problem, but only when content OVERFLOWS: a long
        form whose Save would otherwise scroll out of reach. It is not the
        default, because a bottom-pinned control is how this app says "the
        primary action", and spending that position on a dismiss inverts the
        hierarchy. Material 3 and Apple's HIG both put a full-screen dialog's
        dismiss in the header rather than the footer for the same reason.

        Overflow needs no rule here: the surface is the scroller, so once the
        content is taller than the screen the actions are simply the last thing
        in it and scroll into view with everything else.
      */
      
      .mat-mdc-dialog-content {
        max-height: none;
      }
    }

    &.fs-dialog-mode-peek,
    &.fs-dialog-mode-bottom {
      .mat-mdc-dialog-container {
        border-bottom: calc(fs-style.$safe-area-bottom * .8) solid transparent;
      }
    }

    &.fs-dialog-mode-full,
    &.fs-dialog-mode-peek,
    &.fs-dialog-mode-float,
    &.fs-dialog-mode-bottom {

      &:not(.fs-dialog-mode-float):not(.fs-dialog-mode-peek) {
        .mat-mdc-dialog-container {
          border-radius: 0;
        }
      }

      &:not(.fs-dialog-mode-float) {
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        position: absolute !important;
        max-width: none !important;
        width: auto !important;
        min-width: initial !important;
      }

      &.fs-dialog-mode-peek,
      &.fs-dialog-mode-bottom {
        fs-dialog {
          height: 100%;
          display: flex;
          flex-direction: column;

          .mat-dialog-content {
            max-height: initial;
          }
        }

        .mat-mdc-dialog-container {
          form {
            display: inline;
            min-height: 0;
          }
        }
      }

      &.fs-dialog-mode-bottom {
        top: initial;

        .mat-mdc-dialog-container {
          height: unset;
        }
      }
      
      &.fs-dialog-mode-peek {      
        padding-top: 25px;
      }

      &.fs-dialog-mode-full,
      &.fs-dialog-mode-peek {
        .mat-mdc-dialog-container {
          box-shadow: none;
        }
      }
    }

    /*
      MOBILE STACKS ITS ACTIONS, AND THE MODE SAYS SO - NOT A PIXEL WIDTH.
      Ray 2026-09-15 ("in mobile mode the buttons have to stack... always").

      This was `@media (max-width: $break-xsmall)`, which is 599px, while
      fs-dialog swaps `mode` for `mobileMode` at its own `mobileWidth`, 600px.
      Two numbers deciding two halves of one behaviour, disagreeing by a pixel:
      a dialog 600px wide went into its MOBILE mode - edge to edge, bottom
      sheet - while the stack query stayed unmatched, so the actions kept the
      floating dialog's side-by-side row inside a full-bleed sheet. The gap was
      only ever one pixel wide in theory, but any viewport between the two (a
      narrow desktop window, a tablet in portrait, a phone in landscape) sat in
      it.

      Keying off the mode class closes that by construction. The component
      already decides mobile-or-not in ONE place - the mobileWidth breakpoint
      observer in ngOnInit - and stamps the result on the pane; the stylesheet
      now reads that decision instead of re-deriving it from a number it has to
      keep in sync by hand. Changing mobileWidth on a dialog moves both halves
      together, with nothing here to update.

      FLOAT IS EXCLUDED, and it is the whole reason this is a list rather than
      "any mobile mode". float is the mode a dialog wears when it is still a
      PANEL on a small screen, and Material 3's dialog guidance - text actions
      in a row - is right for it. full, bottom and peek are the three that go
      edge to edge, and those are the ones whose actions become full-width
      targets.
    */
    &.fs-dialog-mode-full,
    &.fs-dialog-mode-bottom,
    &.fs-dialog-mode-peek {
      @include full-width-actions();
      @include full-width-action-appearance();
    }

    .button-layout-full {
      @include full-width-actions();
      @include full-width-action-appearance();
    }
  }

  .fs-dialog-overlay-pane {
    min-width: 400px;

    .mat-dialog-content {
      position: relative;
      z-index: 1;

      /*
        THE SCROLL SHADOW READS THE SURFACE TOKEN, NOT WHITE.

        The two linear-gradients are the top and bottom fades that mask the
        scroll shadows behind them, so they have to be the SAME colour as the
        dialog itself — hardcoded #ffffff, they painted two white bands across
        a dark dialog and the shadow never disappeared.

        --fs-surface-background is the shared contract from
        @firestitch/style (styles/_tokens.scss). THE FALLBACK IS THE LEGACY
        VALUE, NOT THE PACKAGE DEFAULT: an app that does not define the token —
        every existing Material 2 consumer — must render exactly as it did
        before, so the fallback is the #ffffff these lines used to hardcode.
      */
      $surface: var(--fs-surface-background, #ffffff);

      background:
        linear-gradient(#{$surface} 33%, rgba(255,255,255, 0)),
        linear-gradient(rgba(255,255,255, 0), #{$surface} 66%) 0 100%,
        radial-gradient(farthest-side at 50% 0, rgba(34,34,34, 0.2), rgba(0,0,0,0)),
        radial-gradient(farthest-side at 50% 100%, rgba(34,34,34, 0.2), rgba(0,0,0,0)) 0 100%;
      background-color: $surface;
      background-repeat: no-repeat;
      background-attachment: local, local, scroll, scroll;
      background-size: 100% 19px, 100% 19px, 100% 7px, 100% 7px;
      /*
        iOS half pixel horizontal fix
      */
      border-bottom: 1px solid $surface;
    }
  }
}

/*
  The full-screen mode's backdrop IS the page behind the dialog, so it takes
  the surface token rather than white — on a dark app it was a white sheet over
  the whole viewport. Fallback is the legacy , so an app that defines no
  token is unchanged.
*/
.cdk-overlay-backdrop.fs-dialog-overlay-backdrop.fs-dialog-mode-full {
  background: var(--fs-surface-background, white);
}

:root {
  --mat-dialog-with-actions-content-padding: 0 24px;
  --mat-dialog-actions-padding: 16px 24px;
}