/// Returns a list of keys to to pass into the map-set-deep() function
///
/// @author Sam Richard
/// @access public
/// @link https://git.io/vPrlJ
///
/// @param $keys
/// @param $counter
@function get-keys($keys, $counter) {
    $return: ();

    @for $i from 1 to $counter {
        $return: append($return, nth($keys, $i));
    }

    @return $return;
}
/// Determine if a passed color is grayscale
///
/// @author [@esr360](http://twitter.com/esr360)
/// @access public
///
/// @param {color} $color - the color to test for grayscaleness
/// @param {string} $string [''] - string to provide additional/alternative scoping
/// @param {string} $namespace ['grey] - the namespace to search $string for
///
/// @return {bool} - whether or not the passed color is grayscale
@function is-grayscale($color: '', $string: '', $namespace: 'grey') {
    @if (type-of($color) == 'color' and grayscale($color) == $color) or (str-index($string, $namespace)) {
        @return true;
    } @else {
        @return false;
    }
}
/// Remove duplicate values from a list
///
/// @author Hugo Giraudel
/// @access public
/// @link https://goo.gl/ZWDbZo
///
/// @param {list} $list - the list which you want to remove duplicates from
/// @param {bool} $recursive [false] - used if the target list is nested
///
/// @return {list} - updated list
@function list-remove-duplicates($list, $recursive: false) {
    $result: ();

    @each $item in $list {
        @if not index($result, $item) {
            @if length($item) > 1 and $recursive {
                $result: append($result, remove-duplicates($item, $recursive), comma);
            } @else {
                $result: append($result, $item, comma);
            }
        }
    }

    @return $result;
}
/// Remove a value from a list
///
/// @author Hugo Giraudel
/// @access public
/// @link https://goo.gl/LFRwQf
///
/// @param {list} $list - the list which contains the value you wish to remove
/// @param {*} value - the value you wish to remove
/// @param {bool} $recursive [false] - used if the target list is nested
///
/// @return {list} - updated list
@function list-remove($list, $value, $recursive: false) {
    $result: ();

    @for $i from 1 through length($list) {
        @if type-of(nth($list, $i)) == list and $recursive {
            $result: append($result, list-remove(nth($list, $i), $value, $recursive), comma);
        } @else if nth($list, $i) != $value {
            $result: append($result, nth($list, $i), comma);
        }
    }

    @return $result;
}
/// Replace a value in a list
///
/// @author Hugo Giraudel
/// @access public
/// @link https://goo.gl/NCFTHF
///
/// @param {list} $list - the list which contains the value you wish to replace
/// @param {*} $old-value - the value you wish to replace
/// @param {*} $new-value - what you wish to replace the old value with
/// @param {bool} $recursive [false] - used if the target list is nested
///
/// @return {list} - updated list
@function list-replace($list, $old-value, $new-value, $recursive: false) {
    $result: ();

    @for $i from 1 through length($list) {
        @if type-of(nth($list, $i)) == list and $recursive {
            $result: append($result, list-replace(nth($list, $i), $old-value, $new-value, $recursive));
        } @else {
            @if nth($list, $i) == $old-value {
                $result: append($result, $new-value);
            } @else {
                $result: append($result, nth($list, $i));
            }
        }
    }

    @return $result;
}
/// Reverse a list
///
/// @author Hugo Giraudel
/// @access public
/// @link http://hugogiraudel.com/2013/08/08/advanced-sass-list-functions/
///
/// @param {list }$list - the list which you wish to reverse
/// @param {bool} $recursive [false] - enable if target list has nested values
///
/// @return {list} - reversed list
@function list-reverse($list, $recursive: false) {
    $result: ();

    @for $i from length($list) * -1 through -1 {
        @if type-of(nth($list, abs($i))) == list and $recursive {
            $result: append($result, list-reverse(nth($list, abs($i)), $recursive));
        } @else {
            $result: append($result, nth($list, abs($i)));
        }
    }

    @return $result;
}
/// Get a value from a nested map
///
/// @author Hugo Giraudel
/// @access public
/// @link https://www.sitepoint.com/extra-map-functions-sass/
///
/// @param {map} $map - map
/// @param {arglist} $keys - key chain
///
/// @return {*} - desired value
@function map-get-deep($map, $keys...) {
    @each $key in $keys {
        $map: map-get($map, $key);
    }
    @return $map;
}
/// Used to recursively merge (deep merge) two maps
///
/// @author Zsolt Pentz
/// @access public
/// @link https://codepen.io/pentzzsolt/pen/yJkgyW
///
/// @param {map} $map-old - The original map
/// @param {map} $map-new - The new map you wish to merge into the original
///
/// @return {map} - merged map
@function map-merge-deep($parent-map, $child-map) {
    $result: $parent-map;

    @each $key, $value in $child-map {
        @if(type-of(map-get($result, $key)) == map and type-of($value) == map) {
            $result: map-merge($result, ($key: map-merge-deep(map-get($result, $key), $value)));
        } @else {
            $result: map-merge($result, ($key: $value));
        }
    }

    @return $result;
}
/// Reverse a map
///
/// @author Sean McEmerson
/// @access public
/// @link http://git.io/vLfuz
///
/// @param {map} $map - the map to be reversed
///
/// @return {map} - reversed map
@function map-reverse($map) {
    $keys: map-keys($map);
    $map-reversed: ();

    @for $i from length($keys) through 1 {
        $map-reversed: map-merge(
            $map-reversed, (nth($keys, $i): map-get($map, nth($keys, $i)))
        );
    }

    $result: $map-reversed;

    @return $result;
}
/// Get the value of a unique key from a nested map
///
/// @author [@esr360](http://twitter.com/esr360)
/// @access public
///
/// @param {map} $map - the map which contains the target key
/// @param {string} $target-key - the key of interest
/// @param {*} $target-value - optional fallback value if key not found
///
/// @return {*} - desired value
@function map-search($map, $target-key, $target-value: '') {
    @each $key, $value in $map {
        // Is this our key?
        @if $key == $target-key {
            $target-value: $value;
        } @else {
            @if type-of($value) == 'map' {
                // Does the new map contain our key?
                @if map-has-key($value, $target-key) {
                    $target-value: map-get($value, $target-key);
                } @else {
                    // if not, recurse the function
                    $target-value: map-search($value, $target-key, $target-value);
                }
            }
        }
    }
    @return $target-value;
}
/// Set a nested key in an existing map
///
/// @author Sam Richard
/// @access public
/// @link https://git.io/vPr80
///
/// @param {map} $map - the map which contains the key you wish to set
/// @param {string|list} $keys - the keychain trail to your desired key
/// @param {*} $value - the value you wish to set for the key
///
/// @return {map} - updated map
@function map-set-deep($map, $keys, $value) {

    $private-sassy-maps-suppress-warnings: true !global;
    $length: length($keys);
    $get-keys: ();
    $map-level: ();

    @if $length > 1 {
        $get-keys: get-keys($keys, $length);
        $map-level: map-get-deep($map, $get-keys);
    }

    $merge: (nth($keys, $length): $value);

    @if $map-level {
        $merge: map-merge($map-level, $merge);
    }

    @for $i from ($length * -1 + 1) through -1 {
        $j: abs($i);
        $key: nth($keys, $j);
        @if $j > 1 {
            $get-keys: get-keys($keys, $j);
            $map-level: map-get-deep($map, $get-keys);
            @if $map-level {
                $merge: map-merge($map-level, ($key: $merge));
            } @else {
                $merge: ($key: $merge);
            }
        } @else {
            $merge: ($key: $merge);
        }
    }

    $map: map-merge($map, $merge);
    $private-sassy-maps-suppress-warnings: false !global;

    @return $map;
}
/// Set a key in an existing map
///
/// @author Sam Richard
/// @access public
/// @link https://git.io/vPr8B
///
/// @param {map} $map - the map which contains the key you wish to set
/// @param {string} $key - the key you wish to set
/// @param {*} $value - the value you wish to set for the key
///
/// @return {map} - updated map
@function map-set($map, $key, $value) {
    @return map-merge($map, ($key: $value));
}
/// Multiply one number by the power of another
///
/// @author Hugo Giraudel
/// @access public
/// @link https://css-tricks.com/snippets/sass/power-function/
///
/// @param {number} $number - the number you wish to multiply
/// @param {number} $exponent - the power by which to multiply
///
/// @return {number} - result of $number ^ $exponent
@function pow($number, $exponent) {
    $value: 1;

    @if $exponent > 0 {
        @for $i from 1 through $exponent {
            $value: $value * $number;
        }
    } @else if $exponent < 0 {
        @for $i from 1 through -$exponent {
            $value: $value / $number;
        }
    }

    @return $value;
}
/// Replace `$search` with `$replace` in `$string`
///
/// @author Hugo Giraudel
/// @access public
/// @link https://css-tricks.com/snippets/sass/str-replace-function/
///
/// @param {string} $string - initial string
/// @param {string} $search - substring to replace
/// @param {string} $replace - new value
///
/// @return {string} - updated string
@function str-replace($string, $search, $replace: '') {
    $index: str-index($string, $search);

    @if $index {
        @return str-slice($string, 1, $index - 1) + $replace + str-replace(
            str-slice($string, $index + str-length($search)), $search, $replace
        );
    }

    @return $string;
}
/// Remove the units from a value
///
/// @author Hugo Giraudel
/// @access public
/// @link https://css-tricks.com/snippets/sass/strip-unit-function/
///
/// @param {number} $value - number to remove unit from
///
/// @return {number} - Unitless number
@function strip-unit($value) {
    @return $value / ($value * 0 + 1);
}
// Prepend all selectors with a namespace
$moduleNamespace: null !default;

// Set the glue to chain modifiers to modules
$modifierGlue: '--' !default;

// Set the glue to chain components to modules
$componentGlue: '__' !default;

// Extend each module option as a modifier by default?
$extendOptions: true !default;

// Custom parser to use for configuration
$sassConfigParser: null !default;

// Automatically generate CSS from module configuration
$outputCSSFromConfig: false !default;

// The variable to hold a single module (do not edit)
$module: null !default;
/// Cell Atom - Display property
///
/// @param {list} $context - The desired context/condition
/// @param {*} $default - The default value to use when the context is not met
/// @param {*} $value - The value to use when the context is met
@mixin display($context, $value, $default) {
  display: $default;

  @include context($context...) {
    display: $value;
  }
}
/// Cell Atom - Position property
///
/// @param {list} $context - The desired context/condition
/// @param {*} $default - The default value to use when the context is not met
/// @param {*} $value - The value to use when the context is met
@mixin position($context, $value, $default) {
  position: $default;

  @include context($context...) {
    position: $value;
  }
}
/// Cell Atom - Visibility property
///
/// @param {list} $context - The desired context/condition
/// @param {*} $default - The default value to use when the context is not met
/// @param {*} $value - The value to use when the context is met
@mixin visibility($context, $value, $default) {
  visibility: $default;

  @include context($context...) {
    visibility: $value;
  }
}
/// Used to merge a module's default configuration with any custom
/// values passed to the module
///
/// @param {map} $map-old - The map which holds default configuration
/// @param {map} $map-new - The map which holds custom values
/// @param {string} $parser - Custom parser to use for configuration
@function create-config($map-old: (), $map-new: (), $parser: $sassConfigParser) {
  $map-old: map-merge((
    'extendOptions' : $extendOptions
  ), $map-old);

  // Merge theme values
  @if variable-exists('theme') and not variable-exists('CellThemeProcessed') {
    @if map-has-key($theme, 'modules') {
      $map-old: map-merge-deep($map-old, map-get-deep($theme, 'modules', map-get($map-old, 'name')));
    }
  }

  // Merge default and custom options
  $map-merged: map-merge-deep($map-old, $map-new);

  // Evaluate configuration values
  $map-merged: eval-config($map-merged);

  // Evaluate configuration values
  @if ($parser) {
    $map-merged: call(get-function($parser), $map-merged);
  }

  // Store config in global variable
  $config: $map-merged !global;
  $smart-config: smart-config($config) !global;

  // Return merged map
  @return $map-merged;
}

/// Evaluate properties from function-calls
@function eval-config($config) {
  $evaluated-config: ();

  @each $key, $value in $config {
    @if type-of($value) == 'map' {
      $evaluated-config: map-set($evaluated-config, $key, eval-config($value));
    }

    @else {
      @if type-of($value) == 'list' and type-of(nth($value, 1)) == 'string' and function-exists(nth($value, 1)) {
        $value: call(get-function(nth($value, 1)), nth($value, 2)...);
      }

      $evaluated-config: map-set($evaluated-config, $key, $value);
    }
  }

  @return $evaluated-config;
}

/// Convert configuration keys such as `component(item)` into the
/// paramater name, i.e. `item`, so they can be retreived with `this()`
@function smart-config($config) {
  $smart-config: ();

  @each $key, $value in $config {
    @if type-of($value) == 'map' {
      $smart-config: map-set($smart-config, get-param($key), smart-config($value));
    }

    @else {
      @if type-of($key) == 'string' {
        $key: get-param($key);
      }

      $smart-config: map-set($smart-config, $key, $value);
    }
  }

  @return $smart-config;
}
/// Used to create a selector from context() arguments
///
/// @param {(string|list)} $block - The name of the module/component that has the context
/// @param {string} $context - The context you wish to test
/// @param {bool} $pipeContexts
@function create-selector-from-context($block, $context: null, $pipeContexts: false) {
  $selector: ();
  $pipedContexts: '';
  $excludeRootSelector: false;

  @if $context == '' {
    $context: null;
  }

  @if $pipeContexts {
    @each $item in $context {
      @if str-index($item, ':') == 1 {
        $pipedContexts: $pipedContexts + $item;
      }
      @else {
        $excludeRootSelector: true;
        $pipedContexts: $pipedContexts + '[class*="#{$modifierGlue}#{$item}"]';
      }
    }
    @if type-of($block) == 'string' and not $excludeRootSelector {
      $selector: '.#{$block}#{$pipedContexts}, [class*="#{$block}#{$modifierGlue}"]#{$pipedContexts}';
    }
    @else {
      $selector: '#{generate-chunk($block)}#{$pipedContexts}';
    }
  }

  @else {
    @if $context {
      @each $item in $context {
        @if str-index($item, ':') == 1 {
          @if type-of($block) == 'string' {
            $selector: append($selector, '.#{$block}#{$item}', comma);
          }
          $selector: append($selector, '#{generate-chunk($block)}#{$item}', comma);
        }
        @else {
          $selector: append($selector, '#{generate-chunk($block)}[class*="#{$modifierGlue}#{$item}"]', comma);
        }
      }
    }
    @else {
      @if type-of($block) == 'string' {
        $selector: append($selector, '.#{$block}', comma);
        $selector: append($selector, '[class*="#{$block}#{$modifierGlue}"]', comma);
      }
      @else {
        $selector: append($selector, generate-chunk($block), comma);
      }
    }
  }

  @return $selector;
}

/// Generate a selector chunk from a $block list
///
/// @param {(string|list)} $block
@function generate-chunk($block) {
  $chunk: '';

  @if type-of($block) == 'list' {
    @for $i from 1 through length($block) {
      @if $i == 1 {
        $chunk: $chunk + '[class*="#{nth($block, $i)}#{$componentGlue}"]';
      }
      @else {
        $chunk: $chunk + '[class*="#{$componentGlue}#{nth($block, $i)}"]';
      }
    }
  }
  @else {
    $chunk: '[class*="#{$block}#{$modifierGlue}"]';
  }

  @return $chunk;
}
/// List of known and valid CSS properties
///
/// @type Map
$css-properties: (
  'accelerator',
  '-wap-accesskey',
  'align-content',
  '-webkit-align-content',
  'align-items',
  '-webkit-align-items',
  'align-self',
  '-webkit-align-self',
  'alignment-baseline',
  'all',
  'alt',
  '-webkit-alt',
  'animation',
  'animation-delay',
  '-moz-animation-delay',
  '-ms-animation-delay',
  '-webkit-animation-delay',
  'animation-direction',
  '-moz-animation-direction',
  '-ms-animation-direction',
  '-webkit-animation-direction',
  'animation-duration',
  '-moz-animation-duration',
  '-ms-animation-duration',
  '-webkit-animation-duration',
  'animation-fill-mode',
  '-moz-animation-fill-mode',
  '-ms-animation-fill-mode',
  '-webkit-animation-fill-mode',
  'animation-iteration-count',
  '-moz-animation-iteration-count',
  '-ms-animation-iteration-count',
  '-webkit-animation-iteration-count',
  '-moz-animation',
  '-ms-animation',
  'animation-name',
  '-moz-animation-name',
  '-ms-animation-name',
  '-webkit-animation-name',
  'animation-play-state',
  '-moz-animation-play-state',
  '-ms-animation-play-state',
  '-webkit-animation-play-state',
  'animation-timing-function',
  '-moz-animation-timing-function',
  '-ms-animation-timing-function',
  '-webkit-animation-timing-function',
  '-webkit-animation',
  '-webkit-app-region',
  'appearance',
  '-moz-appearance',
  '-webkit-appearance',
  '-webkit-aspect-ratio',
  'audio-level',
  'azimuth',
  'backdrop-filter',
  '-webkit-backdrop-filter',
  'backface-visibility',
  '-moz-backface-visibility',
  '-ms-backface-visibility',
  '-webkit-backface-visibility',
  'background',
  'background-attachment',
  '-webkit-background-attachment',
  'background-blend-mode',
  'background-clip',
  '-moz-background-clip',
  '-webkit-background-clip',
  'background-color',
  '-webkit-background-color',
  '-webkit-background-composite',
  'background-image',
  'background-image-transform',
  '-webkit-background-image',
  '-moz-background-inline-policy',
  'background-origin',
  '-moz-background-origin',
  '-webkit-background-origin',
  'background-position',
  '-webkit-background-position',
  'background-position-x',
  '-webkit-background-position-x',
  'background-position-y',
  '-webkit-background-position-y',
  'background-repeat',
  '-webkit-background-repeat',
  'background-repeat-x',
  'background-repeat-y',
  'background-size',
  '-moz-background-size',
  '-webkit-background-size',
  '-webkit-background',
  'baseline-shift',
  'behavior',
  '-moz-binding',
  '-ms-block-progression',
  'block-size',
  'block-step',
  'block-step-align',
  'block-step-insert',
  'block-step-round',
  'block-step-size',
  'bookmark-label',
  'bookmark-level',
  'bookmark-state',
  'border',
  '-webkit-border-after-color',
  '-webkit-border-after-style',
  '-webkit-border-after',
  '-webkit-border-after-width',
  '-webkit-border-before-color',
  '-webkit-border-before-style',
  '-webkit-border-before',
  '-webkit-border-before-width',
  'border-block',
  'border-block-color',
  'border-block-end',
  'border-block-end-color',
  'border-block-end-style',
  'border-block-end-width',
  'border-block-start',
  'border-block-start-color',
  'border-block-start-style',
  'border-block-start-width',
  'border-block-style',
  'border-block-width',
  'border-bottom',
  'border-bottom-color',
  '-moz-border-bottom-colors',
  'border-bottom-left-radius',
  '-webkit-border-bottom-left-radius',
  'border-bottom-right-radius',
  '-webkit-border-bottom-right-radius',
  'border-bottom-style',
  'border-bottom-width',
  'border-boundary',
  'border-collapse',
  'border-color',
  '-moz-border-end-color',
  '-webkit-border-end-color',
  '-moz-border-end',
  '-moz-border-end-style',
  '-webkit-border-end-style',
  '-webkit-border-end',
  '-moz-border-end-width',
  '-webkit-border-end-width',
  '-webkit-border-fit',
  '-webkit-border-horizontal-spacing',
  'border-image',
  '-moz-border-image',
  '-o-border-image',
  'border-image-outset',
  '-webkit-border-image-outset',
  'border-image-repeat',
  '-webkit-border-image-repeat',
  'border-image-slice',
  '-webkit-border-image-slice',
  'border-image-source',
  '-webkit-border-image-source',
  'border-image-transform',
  '-webkit-border-image',
  'border-image-width',
  '-webkit-border-image-width',
  'border-inline',
  'border-inline-color',
  'border-inline-end',
  'border-inline-end-color',
  'border-inline-end-style',
  'border-inline-end-width',
  'border-inline-start',
  'border-inline-start-color',
  'border-inline-start-style',
  'border-inline-start-width',
  'border-inline-style',
  'border-inline-width',
  'border-left',
  'border-left-color',
  '-moz-border-left-colors',
  'border-left-style',
  'border-left-width',
  'border-radius',
  '-moz-border-radius-bottomleft',
  '-moz-border-radius-bottomright',
  '-moz-border-radius',
  '-moz-border-radius-topleft',
  '-moz-border-radius-topright',
  '-webkit-border-radius',
  'border-right',
  'border-right-color',
  '-moz-border-right-colors',
  'border-right-style',
  'border-right-width',
  'border-spacing',
  '-moz-border-start-color',
  '-webkit-border-start-color',
  '-moz-border-start',
  '-moz-border-start-style',
  '-webkit-border-start-style',
  '-webkit-border-start',
  '-moz-border-start-width',
  '-webkit-border-start-width',
  'border-style',
  'border-top',
  'border-top-color',
  '-moz-border-top-colors',
  'border-top-left-radius',
  '-webkit-border-top-left-radius',
  'border-top-right-radius',
  '-webkit-border-top-right-radius',
  'border-top-style',
  'border-top-width',
  '-webkit-border-vertical-spacing',
  'border-width',
  'bottom',
  '-moz-box-align',
  '-webkit-box-align',
  'box-decoration-break',
  '-webkit-box-decoration-break',
  '-moz-box-direction',
  '-webkit-box-direction',
  '-webkit-box-flex-group',
  '-moz-box-flex',
  '-webkit-box-flex',
  '-webkit-box-lines',
  '-moz-box-ordinal-group',
  '-webkit-box-ordinal-group',
  '-moz-box-orient',
  '-webkit-box-orient',
  '-moz-box-pack',
  '-webkit-box-pack',
  '-webkit-box-reflect',
  'box-shadow',
  '-moz-box-shadow',
  '-webkit-box-shadow',
  'box-sizing',
  '-moz-box-sizing',
  '-webkit-box-sizing',
  'box-snap',
  'break-after',
  'break-before',
  'break-inside',
  'buffered-rendering',
  'caption-side',
  'caret',
  'caret-animation',
  'caret-color',
  'caret-shape',
  'chains',
  'clear',
  'clip',
  'clip-path',
  '-webkit-clip-path',
  'clip-rule',
  'color',
  'color-adjust',
  '-webkit-color-correction',
  'color-interpolation',
  'color-interpolation-filters',
  'color-profile',
  'color-rendering',
  '-webkit-column-axis',
  '-webkit-column-break-after',
  '-webkit-column-break-before',
  '-webkit-column-break-inside',
  'column-count',
  '-moz-column-count',
  '-webkit-column-count',
  'column-fill',
  '-moz-column-fill',
  '-webkit-column-fill',
  'column-gap',
  '-moz-column-gap',
  '-webkit-column-gap',
  'column-progression',
  '-webkit-column-progression',
  'column-rule',
  'column-rule-color',
  '-moz-column-rule-color',
  '-webkit-column-rule-color',
  '-moz-column-rule',
  'column-rule-style',
  '-moz-column-rule-style',
  '-webkit-column-rule-style',
  '-webkit-column-rule',
  'column-rule-width',
  '-moz-column-rule-width',
  '-webkit-column-rule-width',
  'column-span',
  '-webkit-column-span',
  'column-width',
  '-moz-column-width',
  '-webkit-column-width',
  'columns',
  '-moz-columns',
  '-webkit-columns',
  '-webkit-composition-fill-color',
  '-webkit-composition-frame-color',
  'contain',
  'content',
  '-ms-content-zoom-chaining',
  '-ms-content-zoom-limit-max',
  '-ms-content-zoom-limit-min',
  '-ms-content-zoom-limit',
  '-ms-content-zoom-snap',
  '-ms-content-zoom-snap-points',
  '-ms-content-zoom-snap-type',
  '-ms-content-zooming',
  'continue',
  'counter-increment',
  'counter-reset',
  'counter-set',
  'cue',
  'cue-after',
  'cue-before',
  'cursor',
  '-webkit-cursor-visibility',
  'cx',
  'cy',
  'd',
  '-apple-dashboard-region',
  '-webkit-dashboard-region',
  'direction',
  'display',
  'display-align',
  'dominant-baseline',
  'elevation',
  'empty-cells',
  'enable-background',
  'fill',
  'fill-break',
  'fill-color',
  'fill-image',
  'fill-opacity',
  'fill-origin',
  'fill-position',
  'fill-repeat',
  'fill-rule',
  'fill-size',
  'filter',
  '-ms-filter',
  '-webkit-filter',
  'flex',
  '-ms-flex-align',
  '-webkit-flex-align',
  'flex-basis',
  '-webkit-flex-basis',
  'flex-direction',
  '-ms-flex-direction',
  '-webkit-flex-direction',
  'flex-flow',
  '-ms-flex-flow',
  '-webkit-flex-flow',
  'flex-grow',
  '-webkit-flex-grow',
  '-ms-flex-item-align',
  '-webkit-flex-item-align',
  '-ms-flex-line-pack',
  '-webkit-flex-line-pack',
  '-ms-flex',
  '-ms-flex-negative',
  '-ms-flex-order',
  '-webkit-flex-order',
  '-ms-flex-pack',
  '-webkit-flex-pack',
  '-ms-flex-positive',
  '-ms-flex-preferred-size',
  'flex-shrink',
  '-webkit-flex-shrink',
  '-webkit-flex',
  'flex-wrap',
  '-ms-flex-wrap',
  '-webkit-flex-wrap',
  'float',
  'float-defer',
  '-moz-float-edge',
  'float-offset',
  'float-reference',
  'flood-color',
  'flood-opacity',
  'flow',
  'flow-from',
  '-ms-flow-from',
  '-webkit-flow-from',
  'flow-into',
  '-ms-flow-into',
  '-webkit-flow-into',
  'font',
  'font-display',
  'font-family',
  'font-feature-settings',
  '-moz-font-feature-settings',
  '-ms-font-feature-settings',
  '-webkit-font-feature-settings',
  'font-kerning',
  '-webkit-font-kerning',
  'font-language-override',
  '-moz-font-language-override',
  'font-max-size',
  'font-min-size',
  'font-optical-sizing',
  'font-palette',
  'font-presentation',
  'font-size',
  'font-size-adjust',
  '-webkit-font-size-delta',
  '-webkit-font-smoothing',
  'font-stretch',
  'font-style',
  'font-synthesis',
  'font-variant',
  'font-variant-alternates',
  'font-variant-caps',
  'font-variant-east-asian',
  'font-variant-ligatures',
  '-webkit-font-variant-ligatures',
  'font-variant-numeric',
  'font-variant-position',
  'font-variation-settings',
  'font-weight',
  'footnote-display',
  'footnote-policy',
  '-moz-force-broken-image-icon',
  'glyph-orientation-horizontal',
  'glyph-orientation-vertical',
  'grid',
  '-webkit-grid-after',
  'grid-area',
  'grid-auto-columns',
  '-webkit-grid-auto-columns',
  'grid-auto-flow',
  '-webkit-grid-auto-flow',
  'grid-auto-rows',
  '-webkit-grid-auto-rows',
  '-webkit-grid-before',
  'grid-column',
  '-ms-grid-column-align',
  'grid-column-end',
  'grid-column-gap',
  '-ms-grid-column',
  '-ms-grid-column-span',
  'grid-column-start',
  '-webkit-grid-column',
  '-ms-grid-columns',
  '-webkit-grid-columns',
  '-webkit-grid-end',
  'grid-gap',
  'grid-row',
  '-ms-grid-row-align',
  'grid-row-end',
  'grid-row-gap',
  '-ms-grid-row',
  '-ms-grid-row-span',
  'grid-row-start',
  '-webkit-grid-row',
  '-ms-grid-rows',
  '-webkit-grid-rows',
  '-webkit-grid-start',
  'grid-template',
  'grid-template-areas',
  'grid-template-columns',
  'grid-template-rows',
  'hanging-punctuation',
  'height',
  '-ms-high-contrast-adjust',
  '-webkit-highlight',
  'hyphenate-character',
  '-webkit-hyphenate-character',
  '-webkit-hyphenate-limit-after',
  '-webkit-hyphenate-limit-before',
  'hyphenate-limit-chars',
  '-ms-hyphenate-limit-chars',
  'hyphenate-limit-last',
  'hyphenate-limit-lines',
  '-ms-hyphenate-limit-lines',
  '-webkit-hyphenate-limit-lines',
  'hyphenate-limit-zone',
  '-ms-hyphenate-limit-zone',
  'hyphens',
  '-moz-hyphens',
  '-ms-hyphens',
  '-webkit-hyphens',
  'image-orientation',
  '-moz-image-region',
  'image-rendering',
  'image-resolution',
  '-ms-ime-align',
  'ime-mode',
  'initial-letter',
  'initial-letter-align',
  '-webkit-initial-letter',
  'initial-letter-wrap',
  'inline-size',
  'input-format',
  '-wap-input-format',
  '-wap-input-required',
  'inset',
  'inset-block',
  'inset-block-end',
  'inset-block-start',
  'inset-inline',
  'inset-inline-end',
  'inset-inline-start',
  '-ms-interpolation-mode',
  'isolation',
  'justify-content',
  '-webkit-justify-content',
  'justify-items',
  'justify-self',
  '-webkit-justify-self',
  'kerning',
  'layout-flow',
  'layout-grid',
  'layout-grid-char',
  'layout-grid-line',
  'layout-grid-mode',
  'layout-grid-type',
  'left',
  'letter-spacing',
  'lighting-color',
  '-webkit-line-align',
  '-webkit-line-box-contain',
  'line-break',
  '-webkit-line-break',
  '-webkit-line-clamp',
  'line-grid',
  '-webkit-line-grid-snap',
  '-webkit-line-grid',
  'line-height',
  'line-height-step',
  'line-increment',
  'line-snap',
  '-webkit-line-snap',
  '-o-link',
  '-o-link-source',
  'list-style',
  'list-style-image',
  'list-style-position',
  'list-style-type',
  '-webkit-locale',
  '-webkit-logical-height',
  '-webkit-logical-width',
  'margin',
  '-webkit-margin-after-collapse',
  '-webkit-margin-after',
  '-webkit-margin-before-collapse',
  '-webkit-margin-before',
  'margin-block',
  'margin-block-end',
  'margin-block-start',
  'margin-bottom',
  '-webkit-margin-bottom-collapse',
  '-webkit-margin-collapse',
  '-moz-margin-end',
  '-webkit-margin-end',
  'margin-inline',
  'margin-inline-end',
  'margin-inline-start',
  'margin-left',
  'margin-right',
  '-moz-margin-start',
  '-webkit-margin-start',
  'margin-top',
  '-webkit-margin-top-collapse',
  'marker',
  'marker-end',
  'marker-knockout-left',
  'marker-knockout-right',
  'marker-mid',
  'marker-offset',
  'marker-pattern',
  'marker-segment',
  'marker-side',
  'marker-start',
  'marks',
  '-wap-marquee-dir',
  'marquee-direction',
  '-webkit-marquee-direction',
  '-webkit-marquee-increment',
  'marquee-loop',
  '-wap-marquee-loop',
  '-webkit-marquee-repetition',
  'marquee-speed',
  '-wap-marquee-speed',
  '-webkit-marquee-speed',
  'marquee-style',
  '-wap-marquee-style',
  '-webkit-marquee-style',
  '-webkit-marquee',
  'mask',
  '-webkit-mask-attachment',
  'mask-border',
  'mask-border-mode',
  'mask-border-outset',
  'mask-border-repeat',
  'mask-border-slice',
  'mask-border-source',
  'mask-border-width',
  '-webkit-mask-box-image-outset',
  '-webkit-mask-box-image-repeat',
  '-webkit-mask-box-image-slice',
  '-webkit-mask-box-image-source',
  '-webkit-mask-box-image',
  '-webkit-mask-box-image-width',
  'mask-clip',
  '-webkit-mask-clip',
  'mask-composite',
  '-webkit-mask-composite',
  'mask-image',
  '-webkit-mask-image',
  'mask-mode',
  'mask-origin',
  '-webkit-mask-origin',
  'mask-position',
  '-webkit-mask-position',
  'mask-position-x',
  '-webkit-mask-position-x',
  'mask-position-y',
  '-webkit-mask-position-y',
  'mask-repeat',
  '-webkit-mask-repeat',
  '-webkit-mask-repeat-x',
  '-webkit-mask-repeat-y',
  'mask-size',
  '-webkit-mask-size',
  'mask-source-type',
  '-webkit-mask-source-type',
  'mask-type',
  '-webkit-mask',
  '-webkit-match-nearest-mail-blockquote-color',
  'max-block-size',
  'max-height',
  'max-inline-size',
  'max-lines',
  '-webkit-max-logical-height',
  '-webkit-max-logical-width',
  'max-width',
  'max-zoom',
  'min-block-size',
  'min-height',
  'min-inline-size',
  '-webkit-min-logical-height',
  '-webkit-min-logical-width',
  'min-width',
  'min-zoom',
  'mix-blend-mode',
  'motion',
  'motion-offset',
  'motion-path',
  'motion-rotation',
  'nav-down',
  'nav-index',
  'nav-left',
  'nav-right',
  'nav-up',
  '-webkit-nbsp-mode',
  'object-fit',
  '-o-object-fit',
  'object-position',
  '-o-object-position',
  'offset',
  'offset-after',
  'offset-anchor',
  'offset-before',
  'offset-block-end',
  'offset-block-start',
  'offset-distance',
  'offset-end',
  'offset-inline-end',
  'offset-inline-start',
  'offset-path',
  'offset-position',
  'offset-rotate',
  'offset-rotation',
  'offset-start',
  'opacity',
  '-moz-opacity',
  'order',
  '-webkit-order',
  '-moz-orient',
  'orientation',
  'orphans',
  '-moz-osx-font-smoothing',
  'outline',
  'outline-color',
  '-moz-outline-color',
  '-moz-outline',
  'outline-offset',
  '-moz-outline-offset',
  '-moz-outline-radius-bottomleft',
  '-moz-outline-radius-bottomright',
  '-moz-outline-radius',
  '-moz-outline-radius-topleft',
  '-moz-outline-radius-topright',
  'outline-style',
  '-moz-outline-style',
  'outline-width',
  '-moz-outline-width',
  'overflow',
  'overflow-anchor',
  '-webkit-overflow-scrolling',
  'overflow-style',
  '-ms-overflow-style',
  'overflow-wrap',
  'overflow-x',
  'overflow-y',
  'padding',
  '-webkit-padding-after',
  '-webkit-padding-before',
  'padding-block',
  'padding-block-end',
  'padding-block-start',
  'padding-bottom',
  '-moz-padding-end',
  '-webkit-padding-end',
  'padding-inline',
  'padding-inline-end',
  'padding-inline-start',
  'padding-left',
  'padding-right',
  '-moz-padding-start',
  '-webkit-padding-start',
  'padding-top',
  'page',
  'page-break-after',
  'page-break-before',
  'page-break-inside',
  'paint-order',
  'pause',
  'pause-after',
  'pause-before',
  'perspective',
  '-moz-perspective',
  '-ms-perspective',
  'perspective-origin',
  '-moz-perspective-origin',
  '-ms-perspective-origin',
  '-webkit-perspective-origin',
  'perspective-origin-x',
  '-webkit-perspective-origin-x',
  'perspective-origin-y',
  '-webkit-perspective-origin-y',
  '-webkit-perspective',
  'pitch',
  'pitch-range',
  'place-content',
  'place-items',
  'place-self',
  'play-during',
  'pointer-events',
  'position',
  '-webkit-print-color-adjust',
  'quotes',
  'r',
  '-webkit-region-break-after',
  '-webkit-region-break-before',
  '-webkit-region-break-inside',
  'region-fragment',
  '-webkit-region-fragment',
  '-webkit-region-overflow',
  'resize',
  'rest',
  'rest-after',
  'rest-before',
  'richness',
  'right',
  'rotate',
  'rotation',
  'rotation-point',
  '-webkit-rtl-ordering',
  'ruby-align',
  'ruby-merge',
  'ruby-overhang',
  'ruby-position',
  '-webkit-ruby-position',
  'running',
  'rx',
  'ry',
  'scale',
  'scroll-behavior',
  '-ms-scroll-chaining',
  '-ms-scroll-limit',
  '-ms-scroll-limit-x-max',
  '-ms-scroll-limit-x-min',
  '-ms-scroll-limit-y-max',
  '-ms-scroll-limit-y-min',
  'scroll-padding',
  'scroll-padding-block',
  'scroll-padding-block-end',
  'scroll-padding-block-start',
  'scroll-padding-bottom',
  'scroll-padding-inline',
  'scroll-padding-inline-end',
  'scroll-padding-inline-start',
  'scroll-padding-left',
  'scroll-padding-right',
  'scroll-padding-top',
  '-ms-scroll-rails',
  'scroll-snap-align',
  'scroll-snap-coordinate',
  '-webkit-scroll-snap-coordinate',
  'scroll-snap-destination',
  '-webkit-scroll-snap-destination',
  'scroll-snap-margin',
  'scroll-snap-margin-block',
  'scroll-snap-margin-block-end',
  'scroll-snap-margin-block-start',
  'scroll-snap-margin-bottom',
  'scroll-snap-margin-inline',
  'scroll-snap-margin-inline-end',
  'scroll-snap-margin-inline-start',
  'scroll-snap-margin-left',
  'scroll-snap-margin-right',
  'scroll-snap-margin-top',
  'scroll-snap-points-x',
  '-ms-scroll-snap-points-x',
  '-webkit-scroll-snap-points-x',
  'scroll-snap-points-y',
  '-ms-scroll-snap-points-y',
  '-webkit-scroll-snap-points-y',
  'scroll-snap-stop',
  'scroll-snap-type',
  '-ms-scroll-snap-type',
  '-webkit-scroll-snap-type',
  'scroll-snap-type-x',
  'scroll-snap-type-y',
  '-ms-scroll-snap-x',
  '-ms-scroll-snap-y',
  '-ms-scroll-translation',
  'scrollbar-arrow-color',
  'scrollbar-base-color',
  'scrollbar-dark-shadow-color',
  'scrollbar-darkshadow-color',
  'scrollbar-face-color',
  'scrollbar-gutter',
  'scrollbar-highlight-color',
  'scrollbar-shadow-color',
  'scrollbar-track-color',
  'scrollbar3d-light-color',
  'scrollbar3dlight-color',
  'shape-image-threshold',
  '-webkit-shape-image-threshold',
  'shape-inside',
  '-webkit-shape-inside',
  'shape-margin',
  '-webkit-shape-margin',
  'shape-outside',
  '-webkit-shape-outside',
  '-webkit-shape-padding',
  'shape-rendering',
  'size',
  'snap-height',
  'solid-color',
  'solid-opacity',
  'speak',
  'speak-as',
  'speak-header',
  'speak-numeral',
  'speak-punctuation',
  'speech-rate',
  'src',
  '-moz-stack-sizing',
  'stop-color',
  'stop-opacity',
  'stress',
  'string-set',
  'stroke',
  'stroke-align',
  'stroke-alignment',
  'stroke-break',
  'stroke-color',
  'stroke-dash-corner',
  'stroke-dash-justify',
  'stroke-dashadjust',
  'stroke-dasharray',
  'stroke-dashcorner',
  'stroke-dashoffset',
  'stroke-image',
  'stroke-linecap',
  'stroke-linejoin',
  'stroke-miterlimit',
  'stroke-opacity',
  'stroke-origin',
  'stroke-position',
  'stroke-repeat',
  'stroke-size',
  'stroke-width',
  '-webkit-svg-shadow',
  'tab-size',
  '-moz-tab-size',
  '-o-tab-size',
  '-o-table-baseline',
  'table-layout',
  '-webkit-tap-highlight-color',
  'text-align',
  'text-align-all',
  'text-align-last',
  '-moz-text-align-last',
  'text-anchor',
  'text-autospace',
  '-moz-text-blink',
  '-ms-text-combine-horizontal',
  'text-combine-upright',
  '-webkit-text-combine',
  'text-decoration',
  'text-decoration-blink',
  'text-decoration-color',
  '-moz-text-decoration-color',
  '-webkit-text-decoration-color',
  'text-decoration-line',
  '-moz-text-decoration-line',
  'text-decoration-line-through',
  '-webkit-text-decoration-line',
  'text-decoration-none',
  'text-decoration-overline',
  'text-decoration-skip',
  '-webkit-text-decoration-skip',
  'text-decoration-style',
  '-moz-text-decoration-style',
  '-webkit-text-decoration-style',
  'text-decoration-underline',
  '-webkit-text-decoration',
  '-webkit-text-decorations-in-effect',
  'text-emphasis',
  'text-emphasis-color',
  '-webkit-text-emphasis-color',
  'text-emphasis-position',
  '-webkit-text-emphasis-position',
  'text-emphasis-style',
  '-webkit-text-emphasis-style',
  '-webkit-text-emphasis',
  '-webkit-text-fill-color',
  'text-indent',
  'text-justify',
  'text-justify-trim',
  'text-kashida',
  'text-kashida-space',
  'text-line-through',
  'text-line-through-color',
  'text-line-through-mode',
  'text-line-through-style',
  'text-line-through-width',
  'text-orientation',
  '-webkit-text-orientation',
  'text-overflow',
  'text-overline',
  'text-overline-color',
  'text-overline-mode',
  'text-overline-style',
  'text-overline-width',
  'text-rendering',
  '-webkit-text-security',
  'text-shadow',
  'text-size-adjust',
  '-moz-text-size-adjust',
  '-ms-text-size-adjust',
  '-webkit-text-size-adjust',
  'text-space-collapse',
  'text-space-trim',
  'text-spacing',
  '-webkit-text-stroke-color',
  '-webkit-text-stroke',
  '-webkit-text-stroke-width',
  'text-transform',
  'text-underline',
  'text-underline-color',
  'text-underline-mode',
  'text-underline-position',
  '-webkit-text-underline-position',
  'text-underline-style',
  'text-underline-width',
  'text-wrap',
  '-webkit-text-zoom',
  'top',
  'touch-action',
  'touch-action-delay',
  '-ms-touch-action',
  '-webkit-touch-callout',
  '-ms-touch-select',
  'transform',
  'transform-box',
  '-moz-transform',
  '-ms-transform',
  '-o-transform',
  'transform-origin',
  '-moz-transform-origin',
  '-ms-transform-origin',
  '-o-transform-origin',
  '-webkit-transform-origin',
  'transform-origin-x',
  '-webkit-transform-origin-x',
  'transform-origin-y',
  '-webkit-transform-origin-y',
  'transform-origin-z',
  '-webkit-transform-origin-z',
  'transform-style',
  '-moz-transform-style',
  '-ms-transform-style',
  '-webkit-transform-style',
  '-webkit-transform',
  'transition',
  'transition-delay',
  '-moz-transition-delay',
  '-ms-transition-delay',
  '-o-transition-delay',
  '-webkit-transition-delay',
  'transition-duration',
  '-moz-transition-duration',
  '-ms-transition-duration',
  '-o-transition-duration',
  '-webkit-transition-duration',
  '-moz-transition',
  '-ms-transition',
  '-o-transition',
  'transition-property',
  '-moz-transition-property',
  '-ms-transition-property',
  '-o-transition-property',
  '-webkit-transition-property',
  'transition-timing-function',
  '-moz-transition-timing-function',
  '-ms-transition-timing-function',
  '-o-transition-timing-function',
  '-webkit-transition-timing-function',
  '-webkit-transition',
  'translate',
  'uc-alt-skin',
  'uc-skin',
  'unicode-bidi',
  'unicode-range',
  '-webkit-user-drag',
  '-moz-user-focus',
  '-moz-user-input',
  '-moz-user-modify',
  '-webkit-user-modify',
  'user-select',
  '-moz-user-select',
  '-ms-user-select',
  '-webkit-user-select',
  'user-zoom',
  'vector-effect',
  'vertical-align',
  'viewport-fill',
  'viewport-fill-opacity',
  'visibility',
  'voice-balance',
  'voice-duration',
  'voice-family',
  'voice-pitch',
  'voice-range',
  'voice-rate',
  'voice-stress',
  'voice-volume',
  'volume',
  'white-space',
  '-webkit-widget-region',
  'widows',
  'width',
  'will-change',
  '-moz-window-dragging',
  '-moz-window-shadow',
  'word-break',
  'word-spacing',
  'word-wrap',
  'wrap-after',
  'wrap-before',
  'wrap-flow',
  '-ms-wrap-flow',
  '-webkit-wrap-flow',
  'wrap-inside',
  '-ms-wrap-margin',
  '-webkit-wrap-margin',
  '-webkit-wrap-padding',
  '-webkit-wrap-shape-inside',
  '-webkit-wrap-shape-outside',
  'wrap-through',
  '-ms-wrap-through',
  '-webkit-wrap-through',
  '-webkit-wrap',
  'writing-mode',
  '-webkit-writing-mode',
  'x',
  'y',
  'z-index',
  'zoom'
) !default;
/// Return a CSS property if the passed option is enabled
///
/// @param {string|list} $option - the desired option
/// @returns {*} - $true-value if option enabled, otherwise returns $false-value
@function enabled($option, $true-value, $false-value: '') {
  $breadcrumb: ();

  @each $crumb in $option {
    $breadcrumb: join($breadcrumb, $crumb, comma);
  }

  @if value-enabled(map-get-deep($config, $breadcrumb...)) {
    @return $true-value;
  }
  @else {
    @return $false-value;
  }
}
/// Get the name of a module from a namespace
///
/// @param {string} $namespace
@function get-module-name($namespace) {
  $modifier-index: str-index($namespace, $modifierGlue);
  $component-index: str-index($namespace, $componentGlue);

  $slice-index: if($component-index, $component-index, $modifier-index);

  @return if($slice-index, str-slice($namespace, 0, $slice-index - 1), $namespace);
}
/// Get parmater from config string
///
/// @param {string} $source - the string from which to retrieve param
@function get-param($source) {
  @if str-index($source, 'component(') == 1 {
    $source: str-replace($source, 'component(', '');
    $source: str-replace($source, ')', '');
  }

  @else if str-index($source, 'sub-component(') == 1 {
    $source: str-replace($source, 'sub-component(', '');
    $source: str-replace($source, ')', '');
  }

  @else if str-index($source, 'modifier(') == 1 {
    $source: str-replace($source, 'modifier(', '');
    $source: str-replace($source, ')', '');
  }

  @else if str-index($source, $componentGlue) == 1 {
    $source: strip-glue($source, $componentGlue);
  }

  @else if str-index($source, $modifierGlue) == 1 {
    $source: strip-glue($source, $modifierGlue);
  }

  @if type-of($source) == 'string' {
    $source: str-replace($source, "'", '');
  }

  @return $source;
}
/// Merge maps containing module/component CSS
///
/// @param {map} $source
/// @param {map} $target
@function merge-css-maps($source, $target) {
  $content: map-merge-deep($source, $target);

  @if str-index(inspect($content), 'null') == null {
    @each $property, $value in $content {
      $dup-param: null;

      @if str-index($property, $modifierGlue) == 1 {
        $dup-param: $property;
      }

      @each $property, $value in $content {
        @if str-index($property, 'modifier(') == 1 and $dup-param {
          @if get-param($property) == get-param($dup-param) {
            $merge-dup-param: map-merge-deep(map-get($content, $property), map-get($content, $dup-param));
            $content: map-set($content, $dup-param, $merge-dup-param);
            $content: map-remove($content, $property);
          }
        }
      }
    }
  }

  @return $content;
}
/// Create a namespace tree from a passed selector
///
/// @param {list} $selector - The selector from which to create the module tree
@function module-tree($selector) {
  $parent-module: $module;

  // Remove any modifers
  $selectors: remove-modifiers($selector);

  // Remove any junk characters
  $selectors: remove-junk($selectors);

  // Spoof our selectors into a list
  $selectors: str-replace($selectors, ' ', ', ');
  $selectors: selector-parse($selectors);

  @return $selectors;
}
/// Return the value of a module's option
///
/// @alias map-get-deep
@function option($args...) {
  @return map-get-deep($args...);
}
/// Remove junk characters from a selector string
///
/// @param {string} $query
@function remove-junk($query) {
  $query: str-replace($query, '.', '');
  $query: str-replace($query, '[class*="#{$modifierGlue}', '');
  $query: str-replace($query, '[class*="', '');
  $query: str-replace($query, '"]', '');

  @return $query;
}
/// Remove modifiers from a selector
///
/// @param {list} $selector - The selector from which to remove modifiers
@function remove-modifiers($selector) {
  // convert selector to a string
  $selector: inspect(nth($selector, 1));

  $modifier: '';

  // Find out where the first modifier starts
  $modifier-index: str-index($selector, '"#{$modifierGlue}');

  @if $modifier-index {
    // Strip the first part of the selector up until the first modifier
    $modifier: str-slice($selector, $modifier-index);
    // Find out where the modifier ends
    $modifier-end: str-index($modifier, '"]');
    // Isolate the modifier
    $modifier: str-slice($modifier, 0, $modifier-end);
    // Remove the modifier from the selector
    $selector: str-replace($selector, $modifier, '');
    // Remove junk characters
    $selector: str-replace($selector, '[class*=]', '');
    // Recurse the function to eliminate any remainig modifiers
    $selector: remove-modifiers($selector);
  }

  @return $selector;
}
/// Parse CQ
///
/// @param {map} $map - The map from which to parse CQ
/// @param {bool} $sub-component
/// @param {bool} $prevContext
@mixin parse-cq($map, $sub-component: false, $prevContext: false) {
  @each $property, $value in $map {
    @if type-of($property) == 'string' {
      // $property defines new Modifier
      @if str-index($property, 'modifier(') == 1 or str-index($property, $modifierGlue) == 1 {
        @include modifier(get-param($property)) {
          @include parse-cq($value);
        }
      }

      // $property defines new Component
      @else if str-index($property, 'component(') == 1 or str-index($property, $componentGlue) == 1 {
        @include component(get-param($property)) {
          @include parse-cq($value);
        }
      }

      // $property defines new Component
      @else if str-index($property, 'sub-component(') == 1 {
        @include sub-component(get-param($property)) {
          @include parse-cq($value);
        }
      }

      // $property is for parent wrapper/group element
      @else if $property == 'group' or $property == 'wrapper' {
        @include wrapper($property, if($prevContext, $prevContext, $module)) {
          @include parse-cq($value);
        }
      }

      // Determine if current node is queried modifier/state
      @else if str-index($property, 'is-') == 1 {
        @include modifier(str-replace($property, 'is-', '')) {
          @include parse-cq($value);
        }
      }

      // Determine if parent module/block is queried modifier/state
      @else if str-index($property, '$-is-') == 1 or str-index($property, '$:') == 1 {
        $context: if(str-index($property, '$:') == 1, str-replace($property, '$', ''), str-replace($property, '$-is-', ''));

        @include context($module, $context) {
          @include parse-cq($value, $prevContext: ($module, $context));
        }
      }

      // Determine if previously specified parent component is queried modifier/state
      @else if str-index($property, 'and-is-') == 1 or str-index($property, 'and:') == 1 {
        $context: if(
          str-index($property, 'and-is-'),
          str-replace($property, 'and-is-', ''),
          str-slice($property, str-index($property, ':'), str-length($property))
        );
        $prevContextVal: ('block': nth($prevContext, 1), 'context': nth($prevContext, 2));
        $prevChunk: create-selector-from-context(map-get($prevContextVal, 'block'), map-get($prevContextVal, 'context'));
        $newContextVal: append(map-get($prevContextVal, 'context'), $context);
        $newChunk: create-selector-from-context(map-get($prevContextVal, 'block'), $newContextVal, $pipeContexts: true);

        $selector: ();

        @each $pChunk in $prevChunk {
          @each $chunk in selector-parse(str-replace(inspect($newChunk), ' ', ', ')) {
            @if str-index(inspect($chunk), $pChunk) == 1 {
              @each $item in & {
                @if str-index(inspect($item), $pChunk) {
                  $selector: append($selector, selector-replace($item, $pChunk, $chunk), comma);
                }
              }
            }
          }
        }

        @at-root {
          #{$selector} {
            @include parse-cq($value, $prevContext: (map-get($prevContextVal, 'block'), $newContextVal));
          }
        }
      }

      // Determine if specified parent component/module is queried modifier/state
      @else if str-index($property, '-is-') or (str-index($property, ':') and str-index($property, ':') > 1) {
        $component: if(
          str-index($property, ':'),
          str-slice($property, 1, str-index($property, ':') - 1),
          str-slice($property, 1, str-index($property, '-is-') - 1)
        );
        $contextVal: if(
          str-index($property, ':'),
          str-slice($property, str-index($property, ':'), str-length($property)),
          str-slice($property, str-index($property, '-is-') + 4, str-length($property))
        );
        $block: if(str-index($component, '$') == 1, str-slice($component, 2), $module);
        $context: if(str-index($component, '$') == 1, ($block, $contextVal), (($block, $component), $contextVal));

        @if $component == 'group' or $component == 'wrapper' {
          $context: ($component, ($prevContext, $contextVal), true);
        }

        @include context($context...) {
          @include parse-cq($value, $prevContext: $context);
        }
      }

      // Determine if current node is a child of the queried component/module
      @else if str-index($property, 'in-') == 1 {
        $component: str-replace($property, 'in-', '');
        $context: if(str-index($component, '$') == 1, str-slice($component, 2), ($module, $component));

        @include context($context) {
          @include parse-cq($value, $prevContext: ($context, ''));
        }
      }

      // $property defines pseudo-state/pseudo-element
      @else if str-index($property, ':') == 1 {
        @include pseudo-state(str-replace($property, ':', '')) {
          @include parse-cq($value);
        }
      }

      // $property defines .active styles
      @else if $property == 'active' {
        @include modifier('active') {
          @include parse-cq($value);
        }
      }

      // $property is for a component
      @else if type-of($value) == 'map' {
        @include component($property, $sub-component: $sub-component) {
          @include parse-cq($value, $sub-component: true, $prevContext: $property);
        }
      }

      // $property is a valid CSS property
      @else if index($css-properties, $property) {
        @if type-of($value) != 'map' {
          // for before/after pseudo elements
          @if $property == 'content' {
            $value: '"#{$value}"';
          }

          #{$property}: $value;
        }
      }
    }
  }
}
/// Convert a selector into a map of modules, components and modifiers
///
/// @param {string} $selector
@function selector-to-map($selector) {
  $module-map: (
    'module'   : null,
    'component': null,
    'modifiers': null
  );

  // Prepare selector for defragmentation
  $cleaned-selector: inspect(nth($selector, length($selector)));
  $cleaned-selector: selector-parse(str-replace($cleaned-selector, ' ', ', '));
  $cleaned-selector: nth($cleaned-selector, length($cleaned-selector));
  $cleaned-selector: remove-junk(inspect($cleaned-selector));

  // Get and set module name
  $module-map: map-set($module-map, 'module', get-module-name($cleaned-selector));

  // Get and set component name @TODO
  // $component-name: get-component($cleaned-selector);

  // Get and set modifiers @TODO
  // $modifiers: get-modifiers($cleaned-selector);

  @return $module-map;
}
/// Alias function to option()
///
/// @param {map} $map
/// @param {arglist} $keys
@function setting($map, $keys...) {
  @return option($map, $keys...);
}
/// Strip the glue (-- , __) from a string
///
/// @param {string} $string
/// @param {string} $glue
@function strip-glue($string, $glue) {
  $placeholder: random(99999);

  $clean-string: str-replace($string, $string, $placeholder + $string);
  $clean-string: str-replace($clean-string, $placeholder + $glue, '');

  @return $clean-string;
}
/// Get a value from $theme if it exists
///
/// @param {arglist} $args
@function theme($args...) {
  @if variable-exists('theme') and map-get-deep($theme, $args...) {
    @return map-get-deep($theme, $args...);
  }

  @else {
    @return ('theme', $args);
  }
}
/// Get a value from the current module's configuration
///
/// @param {arglist} $options
@function this($options...) {
  $value: '';

  @if (variable-exists('smart-config')) {
    $value: option($smart-config, $options...);
  }

  @else if (variable-exists('config')) {
    $value: option($config, $options...);
  }

  @else {
    @warn 'no config can be found for #{$module}';
  }

  $debug: true;

  @if (variable-exists('sassConfigParser') and $sassConfigParser) {
    $value: call(get-function($sassConfigParser), $value);
  }

  $this: &;

  @if length($this) > 0 {
    @if str-index(inspect(nth($this, 1)), '%') == 1 {
      $debug: false;
    }
  }

  @if $debug and not $value and $value != false {
    @warn '#{$options} not found in #{$module} config';
  }

  @return $value;
}
/// Determine if a passed value should be considered 'enabled'
///
/// @param {*} $value - the value to check
/// @returns {*} - whether or not the option is enabled
@function value-enabled($value) {
  // If the value is a map
  @if type-of($value) == 'map' {
    // Does the map contain the 'enabled' key?
    @if map-has-key($value, 'enabled') {
      @return map-get($value, 'enabled');
    }
    @else {
      @return $value;
    }
  }
  // If the value is a list
  @else if type-of($value) == 'list' {
    // Is the first value a bool?
    @if type-of(nth($value, 1)) == 'bool' {
      @return nth($value, 1);
    }
    @else {
      @return $value;
    }
  }
  @else {
    @return $value;
  }
}
/// Create a new module
///
/// @param {string|list} $modules - The module(s) you wish to create
/// @param {map} $content
@mixin module(
  $modules: if(variable-exists('config'), (map-get($config, 'name')), ''),
  $content: ()
) {
  $config: () !default;

  @if type-of($modules) == 'map' {
    $modules: if(variable-exists('config'), (map-get($config, 'name')), '');
  }

  @if ($moduleNamespace) {
    $modules: $moduleNamespace + $modules;
  }

  // disable any output
  $disable-output: if(variable-exists('disable-output'), $disable-output, false);

  @if variable-exists('config') and map-has-key($config, 'disable-output') {
    $disable-output: map-get($config, 'disable-output');
  }

  // @TODO this needs to identify if nested within itself, not nested in general
  $nested: &;

  // We are creating a root module, so create a global variable
  @if not $nested {
    $module: $modules !global;
    $this: $module !global;
  }

  $selectors: ();

  @each $module in $modules {
    $selectors: join($selectors, '.#{$module}', comma);
    $selectors: join($selectors, '[class*="#{$module}#{$modifierGlue}"]', comma);
  }

  $targetExists: variable-exists('config') and map-has-key($config, 'target');
  // @TODO tidy up how $target is used here and in $modifier - also probably won't work for component()
  $target: if($targetExists, ('module': $module, 'target': map-get($config, 'target')), false) !global;

  @if not $disable-output {
    #{$selectors} {
      @if not $nested {
        @include module-content($modules, $config, $target);
      }

      @content;

      @include parse-cq($content);
    }
  }
}

/// Render a module's content
///
/// @param {string|list} $module
/// @param {map} $config
/// @param {*} $target
@mixin module-content($module, $config, $target) {
  @include extend-modifiers;
  @include combine-modifiers;

  @if variable-exists('config') and $outputCSSFromConfig {
    @if $module == map-get($config, 'name') or $target {
      @if type-of($target) == 'map' and map-get($target, 'module') == $module {
        #{map-get($target, 'target')} {
          @include parse-cq($config);
        }
      }
      @else {
        @include parse-cq($config);
      }
    }
  }
}

/// Alias for module() mixin
///
/// @param {string|list} $modules - The module(s) you wish to create
@mixin modules($modules) {
  @include module($modules) {
    @content;
  }
}

/// Combine modifiers into a new, single modifier
///
/// @param {map} $styles - The CSS styles to output
@mixin combine-modifiers($combine: if(variable-exists('config'), (map-get($config, 'combine')), '()')) {
  @if variable-exists('config') and $combine {
    @each $new-modifier, $target-modifiers in $combine {
      @include modifier($new-modifier) {
        @include extend(($target-modifiers));
      }
    }
  }
}

/// Extend modifiers into the naked module
///
/// @param {map} $styles - The CSS styles to output
@mixin extend-modifiers($modifiers: if(variable-exists('config'), (map-get($config, 'modifiers')), '()')) {
  @if variable-exists('config') and $modifiers {
    @include extend(($modifiers));
  }
}
/// Create a component based off the main module
///
/// @param {string|list} $components - The component or components to be used
/// @param {map} $content
/// @param {bool} $sub-component
/// @param {string} $glue
/// @param {bool} $cascade
@mixin component(
  $components: null,
  $content: (),
  $sub-component: false,
  $glue: $componentGlue,
  $cascade: true
) {
  $this: &;
  $module-map: selector-to-map($this);
  $selectors: '[class*="#{$module}#{$glue}"]';
  $namespace: map-get($module-map, 'module');

  @if str-index($namespace, '%') == 1 {
    $namespace: $module;
  }

  @if $sub-component {
    $namespace: nth(module-tree(&), length(module-tree(&)));
  }

  @if $components {
    $selectors: ();

    @each $component in $components {
      $selector: '.#{$namespace}#{$glue}#{$component}, [class*="#{$namespace}#{$glue}#{$component}#{$modifierGlue}"]';

      @if not $cascade {
        $selector: '> #{$selector}';
      }

      $selectors: join($selectors, $selector, comma);
    }
  }

  $parents: ();

  @each $selector in & {
    // spoof the selector into a list
    $selector: selector-parse(str-replace(inspect($selector), ' ', ', '));

    $is-modifier: str-index(inspect(nth($selector, length($selector))), '[class*="#{$modifierGlue}');
    $is-pseudo-state: str-index(inspect(nth($selector, length($selector))), ':');

    // if the last item isn't a modifier or pseudo state, remove it
    @if not ($is-modifier or $is-pseudo-state) {
      $selector: list-remove($selector, nth($selector, length($selector)));
    }

    @if length($selector) == 1 {
      $selector: nth($selector, 1);
    }

    // Re-build the parent selector
    $parents: append($parents, str-replace(inspect($selector), ', ', ' '), comma);
  }

  $parents: list-remove-duplicates($parents);

  @if length($parents) == 1 {
    $parents: nth($parents, 1);
  }

  @if ($parents == '()') {
    @at-root #{$selectors} {
      @content;

      @include parse-cq($content);
    }
  }
  @else {
    @at-root #{$parents} {
      #{$selectors} {
        @content;

        @include parse-cq($content);
      }
    }
  }
}

/// Alis for `component` mixin with $sub-component: true
///
/// @param {string|list} $components
/// @param {map} $content
/// @param {string} $glue
@mixin sub-component($components: null, $content: (), $glue: $componentGlue) {
  @include component($components, $content, true, $glue) {
    @content;
  }
}

/// Alias for component() mixin
///
/// @author [@esr360](http://twitter.com/esr360)
/// @access public
@mixin components($args...) {
  @include component($args...) {
    @content;
  }
}
/// Create a modifier for a module
///
/// @param {string|list} $modifiers  - The modifier(s) which you wish to create
/// @param {string} $special [null] - Add special contextual options to modifier
/// @param {bool} $glue ['--'] - Desired modifier separator/glue
/// @param {string} $module
@mixin modifier($modifiers, $special: null, $glue: $modifierGlue, $module: $module) {
  $scope: &;

  @if str-index(inspect(&), '.#{$module}') {
    $scope: ();

    @for $i from 1 through length(&) {
      @if $i % 2 == 0 {
        $scope: append($scope, nth(&, $i), comma);
      }
    }
  }

  $modifiers-chunk: ();

  @each $modifier in $modifiers {
    @if $special == 'not' {
      $modifiers-chunk: join($modifiers-chunk, ':not([class*="#{$glue}#{$modifier}"])', comma);
    }
    @else {
      $modifiers-chunk: join($modifiers-chunk, '[class*="#{$glue}#{$modifier}"]', comma);
    }

    $selector: #{$scope}#{$modifiers-chunk};

    // pseudo-elements must be at the end
    @if str-index($selector, ':before') and str-index($selector, ':before') != (str-length($selector) - 6) {
      $selector: str-replace($selector, ':before', '') + ':before';
    }
    @if str-index($selector, ':after') and str-index($selector, ':after') != (str-length($selector) - 5) {
      $selector: str-replace($selector, ':after', '') + ':after';
    }

    @at-root #{$selector} {
      @content;
    }
  }
}

/// Alias for modifier() mixin
@mixin modifiers($args...) {
  @include modifier($args...) {
    @content;
  }
}

/// Alias for modifier() mixin
@mixin is($args...) {
  @include modifier($args...) {
    @content;
  }
}
/// Extend one or more modifiers of a module to create a new modifier
/// combining the ones you pass
///
/// @param {string|list} $modifiers [null] - The modifiers which you wish to combine
/// @param {string} $parent [null] - The parent module if not current one
/// @param {bool} $core [false] - Extend the core '.module' styles?
@mixin extend($modifiers: null, $parent: null, $core: false) {
  $namespaced-parent: if($moduleNamespace and $parent, $moduleNamespace + $parent, $parent);

  @if $core or not $modifiers {
    @extend .#{$namespaced-parent};
  }

  @each $modifier in $modifiers {
    @if type-of($modifier) == 'string' {
      $selector: if($parent, $namespaced-parent, $module);

      @extend [class*="#{$selector}#{$modifierGlue}"][class*="#{$modifierGlue}#{$modifier}"];
    }
    @else if type-of($modifier) == 'list' {
      $namespaced-selector: ('[class*="#{$namespaced-parent}#{$modifierGlue}"]');
      $module-selector: ('[class*="#{$module}#{$modifierGlue}"]');

      $selectors: if($parent, $namespaced-selector, $module-selector);

      @each $item in $modifier {
        $selectors: join($selectors, '[class*="#{$modifierGlue}#{$item}"]', comma);
      }

      @extend #{$selectors};
    }
  }
}

// Alias for extending entire modules
@mixin _module($module, $modifiers: null, $core: true) {
  @include extend($parent: $module, $modifiers: $modifiers, $core: $core);
}
/// Apply styles to a component within a certain context
///
/// @param {arglist} $context
@mixin context($context...) {
  $selector: create-selector-from-context($context...);
  $scope: &;

  @at-root #{$selector} {
    #{$scope} {
      @content;
    }
  }
}
/// Apply styles to a module if a passed option returns true
///
/// @param {arglist} $option-path - The option which you wish to test against
@mixin option($option-path...) {
  $value  : map-get-deep($config, $option-path...);
  $option : nth($option-path, length($option-path)) !global;

  // Output the styles if the option is enabled
  @if value-enabled($value) {
    @content;

    @include parse-cq($value);
  }

  // Create a modifier for the option if it is not enabled
  // by default
  @if $extendOptions and not value-enabled($value) {
    @include modifier($option) {
      @content;

      @include parse-cq($value);
    }
  }
}
/// Used to generate selectors for pseudo states
///
/// @param {string} $state
@mixin pseudo-state($state) {
  $scope: &;
  $selector: ();

  @each $item in $scope {
    $chunk: '#{$item}:#{$state}';

    // pseudo-elements must be at the end
    @if str-index($chunk, ':before') and str-index($chunk, ':before') != (str-length($chunk) - 5) {
      $chunk: str-replace($chunk, ':before', '') + ':before';
    }
    @if str-index($chunk, ':after') and str-index($chunk, ':after') != (str-length($chunk) - 5) {
      $chunk: str-replace($chunk, ':after', '') + ':after';
    }

    $selector: append($selector, $chunk, comma);
  }

  @at-root #{$selector} {
    @content;
  }
}

/// Alias for `pseudo-state` mixin with $state: 'hover'
@mixin hover {
  @include pseudo-state('hover') {
    @content;
  }
}
/// Apply styles when a defined option is a particular value
///
/// @param $value - The value you wish to apply styles to
@mixin value($value) {
  // Create a unique, random placeholder to store styles
  $placeholder : $value + random(9999);
  $get-option: option($config, $option);
  $target: if(type-of($get-option) == 'map', option($config, $option, 'enabled'), $get-option);

  // Determine if the option's value is our value of interest
  $enabled: if($target == $value, true, false);

  @if ($enabled) {
    @content;
  }

  // Create a modifier for the option
  @if $extendOptions and not $enabled {
    @include modifier($option) {
      @include modifier($value) {
        @content;
      }
    }
  }
}
/// Wrap a module or group a collection of modules
///
/// @param {string} $namespace
/// @param {string} $scope
@mixin wrapper($namespace: 'wrapper', $scope: $module) {
  @at-root {
    @include module($namespace) {
      @include modifier($scope, $module: $namespace) {
        @content;
      }
    }
  }
}

/// Alias for wrapper() mixin
@mixin group($scope: $module) {
  @include wrapper('group', $scope) {
    @content;
  }
}