@use 'sass:math';
@use 'sass:color' as sass-color;
@use '../../compiled/tokens/scss/breakpoint';
@use '../../compiled/tokens/scss/color';
@use '../../compiled/tokens/scss/ease';
@use '../../compiled/tokens/scss/line-height';
@use '../../compiled/tokens/scss/opacity';
@use '../../compiled/tokens/scss/scale';
@use '../../compiled/tokens/scss/size';
@use '../../compiled/tokens/scss/transition';
@use '../../mixins/a11y';
@use '../../mixins/fluid';
@use '../../mixins/focus';
@use '../../mixins/ms';
@use '../../mixins/theme';
@use '../../mixins/unit';

// Minimum viewport width where links are exposed
$_breakpoint-wide: breakpoint.$m;

// Minimum viewport width where links are no longer multi-line
$_breakpoint-nowrap: breakpoint.$l;

// Viewport where vertical expansion maxes out
$_breakpoint-grow-max: breakpoint.$xxl;

// Maximum viewport width where links are stacked and hidden initially
$_breakpoint-narrow-max: math.div($_breakpoint-wide * 16 - 1, 16);

// Maximum viewport width where links are multi-line
$_breakpoint-wide-max: math.div($_breakpoint-nowrap * 16 - 1, 16);

// Whitespace used for gaps, padding and margin
// (These use `rem` so they will be consistent throughout the component)
$_space-horizontal: ms.step(1, 1rem);
$_space-vertical: ms.step(-2, 1rem);

// Additional space to apply fluidly at larger breakpoints
$_grow-vertical-min: ms.step(-2, 1rem);
$_grow-vertical-max: ms.step(0, 1rem);

// Padding applied to labels, used for focus outlines
$_label-pad-horizontal: ms.step(-3);
$_label-pad-vertical: ms.step(-9);

// Use the widest standard layout `max-width` plus buffer for horizontal space
$_max-width: #{unit.strip(size.$max-width-spread) +
  unit.strip($_space-horizontal * 3)}em;

// Height of masthead on small screens.
$_masthead-height-sm: ms.step(7);

/**
 * 1. Allow child elements to position themselves relative to the container.
 */
.c-sky-nav {
  position: relative; /* 1 */

  /**
   * Fluidly add more vertical whitespace as the viewport width increases
   */
  @media (min-width: $_breakpoint-wide) {
    padding-block: fluid.fluid-clamp(
      $_grow-vertical-min,
      $_grow-vertical-max,
      $_breakpoint-wide,
      $_breakpoint-grow-max
    );
  }
}

/**
 * "Skip to main content" link, most likely applied to a `c-button` component.
 *
 * 1. Keep links that come later in the source order below this link when
 *    visible.
 */
.c-sky-nav__skip {
  inset-block-start: 50%;
  inset-inline-start: 50%;
  position: absolute;
  white-space: nowrap;
  z-index: 1; /* 1 */

  /**
   * We want to encourage use of `c-button` with this component, but that
   * pattern's `transform` effects may conflict with this one's positioning.
   * To counteract this, we make sure to apply the positioning for mouse effect
   * states.
   */
  &,
  &:hover,
  &:active {
    transform: translate(-50%, -50%);
  }

  /**
   * Only expose the link to screen readers until it is focused.
   */
  &:not(:focus) {
    @include a11y.sr-only;
  }
}

/**
 * Containing element for most nav content.
 *
 * 1. The transition is needed for "skip to main content" focus effect.
 */
.c-sky-nav__content {
  display: flex;
  margin: 0 auto;
  max-inline-size: $_max-width;
  transition: opacity transition.$quick ease.$out; /* 1 */

  /**
   * At narrow sizes, display content in reverse order so that the menu contents
   * will show up first when visible. This sets us up for the "look up to the
   * sky" effect when visible.
   */
  @media (max-width: $_breakpoint-narrow-max) {
    flex-direction: column-reverse;
  }

  /**
   * Fade the nav content when the skip link is visible. This helps visually
   * identify the content that will be skipped and helps the link stand out a
   * little.
   */
  .c-sky-nav__skip:focus ~ & {
    opacity: opacity.$muted;
  }
}

/**
 * Containing element for the logo and menu toggle.
 *
 * 1. Set a fixed height on small screens to support vertically centering the
 *    menu toggle button.
 */
.c-sky-nav__masthead {
  align-items: center;
  block-size: $_masthead-height-sm; /* 1 */
  display: flex;
  justify-content: space-between;

  @media (min-width: $_breakpoint-wide) {
    block-size: auto;
  }
}

/**
 * The logo link element.
 *
 * 1. We apply whitespace to this element directly so its hit area is larger
 *    than it would be if whitespace were applied to the parent element. We use
 *    a bit less space for the logo since it has circular edges that feels
 *    misaligned when only the curves touch the edges.
 * 2. Prevents Flexbox squashing when menu items overflow.
 */
.c-sky-nav__logo {
  align-items: center;
  block-size: 100%; /* 1 */
  display: flex;
  flex: none; /* 2 */
  padding: 0 math.div($_space-horizontal, 1.5); /* 1 */

  @media (min-width: $_breakpoint-wide) {
    block-size: auto;
    display: block;
    padding-block: $_space-vertical; /* 1 */
  }
}

/**
 * The image asset (assumed to be an SVG) within the logo element.
 *
 * 1. Get rid of extra bottom space if this is left as an `inline-block`.
 * 2. Because the logo isn't rectangular, we apply a bit of negative margin all
 *    around for visual balance. We could subtract this value from the container
 *    padding, but it felt easier to understand the scope of the change to me
 *    from this element. (Though now I'm second-guessing myself...)
 * 3. ms.step(9) is used instead of ms.step(8) because ms.step(8) leads to a big jump at the large breakpoint
 */
.c-sky-nav__logo-object {
  block-size: auto;
  display: block; /* 1 */
  fill: currentColor;
  inline-size: fluid.fluid-calc(
    ms.step(7),
    ms.step(9),
    /* 3 */ breakpoint.$s,
    breakpoint.$l
  );
  margin: (ms.step(-6) * -1); /* 2 */
  transition: opacity transition.$slow ease.$out,
    transform transition.$quick ease.$out;
  @media (min-width: breakpoint.$l) {
    inline-size: ms.step(8);
  }
  @media (max-width: breakpoint.$s) {
    inline-size: ms.step(7);
  }

  /**
   * When the containing logo is hovered, scale up this element. Scaling this
   * element instead of the parent helps avoid any accidental overflow or
   * scrollbars.
   */
  .c-sky-nav__logo:hover & {
    transform: scale(scale.$effect-grow);
  }

  /**
   * Similar to the hover effect, except we use `opacity` to convey some depth.
   * This is similar to the use of `filter` in the `c-button` component, except
   * `filter` would desaturate the logo too much in this case.
   */
  .c-sky-nav__logo:active & {
    opacity: opacity.$muted;
    transform: scale(scale.$effect-shrink);
  }
}

/**
 * An outline within the logo SVG we use to enhance its focus state.
 *
 * 1. Invisible by default. We could also use `visibility`, but that can be
 *    less reliable to update and offers no performance benefits.
 * 2. Outline color matches our buttons, inputs, etc.
 */
.c-sky-nav__logo-outline {
  opacity: 0; /* 1 */
  stroke: color.$brand-primary-lighter; /* 2 */
}

/**
 * Expose the outline when the logo element has keyboard focus.
 */
// stylelint-disable-next-line no-duplicate-selectors
.c-sky-nav__logo {
  @include focus.focus-visible {
    .c-sky-nav__logo-outline {
      opacity: 1;
    }
  }
}

/**
 * The element that toggles the menu visibility at narrow widths.
 *
 * 1. This prevents us having to set and then override padding on the masthead
 *    container for this one element.
 */
.c-sky-nav__menu-toggle {
  inset-block-end: calc(
    (#{$_masthead-height-sm} - #{size.$height-control-default}) / 2
  );
  inset-inline-end: $_space-horizontal; /* 1 */
  position: absolute;

  /**
   * Hide once the menu contents are always exposed.
   */
  @media (min-width: $_breakpoint-wide) {
    display: none;
  }

  /**
   * There is no need to show the menu toggle if JS is not available.
   */
  .no-js & {
    display: none;
  }
}

/**
 * Container for the list of menu links, presumably a `<nav>` element.
 *
 * 1. Fill available space even when contents don't require it.
 */
.c-sky-nav__menu {
  flex: 1; /* 1 */

  /**
   * When these links are displayed as a stack and the dark theme is applied,
   * darken the background as if you're looking deeper into the sky up toward
   * spaaaaaaaaaaaace 🚀
   *
   * Fun fact: An earlier version of this design included twinkling stars,
   * but they significantly decreased performance! They also made the text a
   * little harder to read.
   *
   * @todo Decide if supporting themes for SkyNav is even necessary and, if so,
   * try to make this work via custom properties.
   */
  @include theme.styles(dark) {
    @media (max-width: $_breakpoint-narrow-max) {
      background-image: linear-gradient(
        to bottom,
        color.$brand-primary-dark,
        sass-color.change(color.$brand-primary-dark, $alpha: 0)
      );
    }
  }

  /**
   * Once the items are arranged horizontally, we want the links therein to
   * occupy as much height as possible. The simplest way to do that without
   * setting a hard `height` is to make each container along the way a Flexbox.
   */
  @media (min-width: $_breakpoint-wide) {
    display: flex;
  }

  /**
   * Adjustments for when items are arranged horizontally but without enough
   * room to fit on a single row.
   *
   * 1. Right-align the menu items.
   * 2. Constrain the width. This helps discourage orphans. (Cruel, I know.)
   * 3. We apply half whitespace to this container, with the other half on the
   *    actions themselves. This allows them to flow from one row to the next
   *    without worrying about double whitespace or whitespace assigned to
   *    the incorrect edge.
   */
  @media (min-width: $_breakpoint-wide) and (max-width: $_breakpoint-wide-max) {
    margin-inline-start: auto; /* 1 */
    max-inline-size: math.div(100%, 3) * 2; /* 2 */
    padding: math.div($_space-vertical, 2) math.div($_space-horizontal, 2); /* 3 */
  }

  /**
   * When this element is wide enough to show items on a single row, the only
   * whitespace we keep around is a bit on the rightmost side so the last
   * element feels balanced in comparison to those to its left.
   */
  @media (min-width: $_breakpoint-nowrap) {
    padding-inline-end: math.div($_space-horizontal, 2);
  }
}

/**
 * The `<ul>` element containing the actual menu items.
 *
 * 1. Override list defaults. To avoid issues in Safari/VoiceOver, be sure to
 *    include `role="list"` on the element itself!
 *
 * @see https://www.scottohara.me/blog/2019/01/12/lists-and-safari.html
 */
.c-sky-nav__menu-items {
  list-style: none; /* 1 */
  padding-inline-start: 0; /* 1 */

  /**
   * The Sky Nav is progressively enhanced with JS at smaller viewports. By default
   * it is open for when JS is not available. In cases where JS _is_ available,
   * this causes a poor loading UX where the menu jumps from open to closed
   * causing a large layout shift.
   *
   * To keep the progressive enhancement in place _and_ not have the layout shift,
   * we hide the menu items during the "loading" state. The "loading" state
   * happens after the UI renders and before the Sky Nav JS is loaded.
   */
  @media (width < $_breakpoint-wide) {
    .is-loading & {
      display: none;
    }
  }

  /**
   * When the layout is wide enough to expose these items…
   *
   * 1. Display them horizontally.
   * 2. Fill the available space, even if it isn't necessary for the content.
   * 3. If the content wraps or for some reason is not all the way filled, we
   *    should right-align it.
   */
  @media (min-width: $_breakpoint-wide) {
    display: flex; /* 1 */
    flex: 1; /* 2 */
    justify-content: flex-end; /* 3 */
  }

  /**
   * Before we're able to show all the items on one line, we allow them to wrap
   * to a new line.
   */
  @media (min-width: $_breakpoint-wide) and (max-width: $_breakpoint-wide-max) {
    flex-wrap: wrap;
  }
}

/**
 * The `<li>` containing the menu action.
 *
 * 1. Continuing the process of setting `display: flex` on elements that began
 *    with `c-sky-nav__menu` to retain stretchy heights.
 */
.c-sky-nav__menu-item {
  display: flex; /* 1 */

  /**
   * Occupy available space but not more than the content demands relative to
   * other items. Unlike `flex: 1`, this does not attempt to occupy the same
   * amount of space as siblings: Longer items will get more space than shorter
   * ones.
   */
  @media (min-width: $_breakpoint-nowrap) {
    flex: 1 1 auto;
  }
}

/**
 * The link element within the menu, though we name it `-action` just in case
 * there's a future use case for applying these styles to a different
 * interactive element (like a button).
 *
 * 1. Center contents horizontally, vertically and if text happens to break.
 * 2. Fill available height and width.
 * 3. Prevent accidental text selection.
 */
.c-sky-nav__menu-action {
  align-items: center; /* 1 */
  display: flex; /* 2 */
  font-size: size.$font-big;
  inline-size: 100%; /* 2 */
  justify-content: center; /* 1 */
  line-height: line-height.$tight;
  text-align: center; /* 1 */
  text-decoration: none;
  transition: opacity transition.$quick ease.$out;
  user-select: none; /* 3 */

  /**
   * Displayed as a ruled stack on narrow screens
   */
  @media (max-width: $_breakpoint-narrow-max) {
    border-block-end: size.$edge-small solid color.$base-gray-light;
    padding: $_space-vertical $_space-horizontal;

    /**
     * @todo Decide if supporting themes for SkyNav is even necessary and, if so,
     * try to make this work via custom properties.
     */

    @include theme.styles(dark) {
      border-block-end-color: sass-color.change(
        color.$brand-primary-darker,
        $alpha: 0.5
      );
    }
  }

  /**
   * Prevent text wrapping on wider screens where the items themselves are
   * allowed to wrap.
   */
  @media (min-width: $_breakpoint-wide) {
    white-space: nowrap;
  }

  /**
   * When the items are arranged horizontally but may break onto new lines,
   * we apply half the whitespace to these actions. (The other half was applied
   * to `c-sky-nav__menu`.)
   */
  @media (min-width: $_breakpoint-wide) and (max-width: $_breakpoint-wide-max) {
    padding: math.div($_space-vertical, 2) math.div($_space-horizontal, 2);
  }

  /**
   * On wide screens, items that are not being hovered "dim" slightly
   */
  .c-sky-nav__menu-items:hover &:not(:hover) {
    @media (min-width: $_breakpoint-wide) {
      opacity: opacity.$muted;
    }
  }
}

/**
 * The label element is nested in the action so the focus outline may be applied
 * to a smaller area, which gives us a clearer association with the focusable
 * content.
 *
 * 1. We apply half the padding of this element as negative margin to avoid
 *    impacting whitespace of parent elements.
 */
.c-sky-nav__menu-label {
  border-radius: size.$border-radius-full;
  margin: ($_label-pad-vertical * -1) ($_label-pad-horizontal * -1); /* 1 */
  padding: $_label-pad-vertical $_label-pad-horizontal;
}

/**
 * Apply outline to label when actions are focused by keyboard.
 */
// stylelint-disable-next-line no-duplicate-selectors
.c-sky-nav__menu-action {
  @include focus.focus-visible {
    .c-sky-nav__menu-label {
      box-shadow: 0 0 0 size.$edge-large color.$brand-primary-lighter;
    }
  }
}
