/**
** STACK
* 
* A layout that allows to 
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*
*/
.wm-stack {
  display: flex;
  flex-direction: column;
  justify-content: var(--stack-vertical-alignment, flex-start);
}

/* 
Nested variants.

Recursion applies the same margin no matter the nesting depth. 
A more deliberate approach would be to set up alternative 
non-recursive Stacks with different margin values, and nest 
them where suitable.
*/
[class^="wm-stack"] > * {
  /* ↓ top and bottom margins in horizontal-tb writing mode */
  margin-block: 0;
}

/*
We set --stack-space value on the children, not "hoisting"
it up. If the parent is where the property is set, it will get 
overridden if the parent becomes a child in nesting.
*/
.wm-stack > * + * {
  margin-block-start: var(--stack-space, var(--gutter, var(--space-s-m)));
}

.wm-stack-large > * + * {
  --stack-space: 3rem;
}

.wm-stack-small > * + * {
  --stack-space: 0.5rem;
}

/* Splitting the stack by adding an 'auto' margin to a chosen element */
.wm-stack > .wm-stack-split-element {
  margin-block-end: auto;
}

/*
Where the Stack is the only child of its parent, nothing forces 
it to stretch as in the last example/demo. A height of 100% ensures 
the Stack's height matches the parent's and the split can occur.
*/
.wm-stack:only-child {
  /* ↓ `height` in horizontal-tb writing mode */
  block-size: 100%;
}

/* ---------------- END OF STACK ---------------- */

/**
** REPEL
* 
* A layout that allows items to repel from each other where
* space allows.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*
* EXCEPTIONS:
*
* .repel[data-nowrap]: 
*
* .wm-repel[data-repel-variant="vertical"]:
*/
.wm-repel {
  display: flex;
  flex-direction: var(--repel-direction, row);
  flex-wrap: var(--repel-wrap, wrap);
  justify-content: var(--repel-horizontal-alignment, space-between);
  align-items: var(--repel-vertical-alignment, center);
  column-gap: var(--repel-column-gap, var(--gutter, var(--space-s-m)));
  row-gap: var(--repel-row-gap, var(--gutter, var(--space-s-m)));
}

.wm-repel[data-nowrap] {
  flex-wrap: nowrap;
}

.wm-repel[data-repel-variant="vertical"] {
  --repel-direction: column;
  --repel-vertical-alignment: stretch;
}
/* ---------------- END OF REPEL ---------------- */

/**
** CLUSTER
* 
* A layout that allows to configure the elements that differ in length
* into clusters that are liable to wrap.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*
*/
.wm-cluster {
  display: flex;
  flex-direction: var(--cluster-direction, row);
  flex-wrap: var(--cluster-wrap, wrap);
  justify-content: var(--cluster-horizontal-alignment, flex-start);
  align-items: var(--cluster-vertical-alignment, center);
  column-gap: var(--cluster-column-gap, var(--gutter, var(--space-s-m)));
  row-gap: var(--cluster-row-gap, var(--gutter, var(--space-s-m)));
}
/* ---------------- END OF CLUSTER ---------------- */

/** 
** SIDEBAR
*
* A layout that allows to have a flexible main content area
* and a "fixed" width sidebar that sits on the left or right.
* If there is not enough viewport space to fit both the sidebar
* width and the main content minimum width, they will stack
* on top of each other.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*
* --sidebar-gutter (0rem): This defines the space between 
* the sidebar and main content.
*
* --sidebar-target-size (20rem): How large the sidebar should be. 
* Commented means not set; defaults to the content width.
*
* --sidebar-wrap-at (50%): The minimum size of the main content area 
* in relation to the container's size before the wrapping is forced 
* to occur. Value of 50% is apt since a sidebar ceases to be a
* sidebar when it is no longer the narrower of the two elements.
*
* EXCEPTIONS:
*
* [data-direction='rtl']: flips the sidebar to be on the right 
*/
:has(> .wm-sidebar) {
  --sidebar-target-size: 20rem;
  --sidebar-wrap-at: 50%;
  --sidebar-vertical-alignment: flex-start;

  display: flex;
  flex-wrap: wrap;
  gap: var(--sidebar-gutter, var(--gutter, var(--space-s-l)));
}

.wm-sidebar {
  flex-basis: var(--sidebar-target-size);
  align-self: var(--sidebar-vertical-alignment);
  flex-grow: 1;
}

:has(> .wm-sidebar) > :not(.wm-sidebar) {
  /* ↓ Grow from nothing */
  flex-basis: 0;
  flex-grow: 999;
  min-inline-size: var(--sidebar-wrap-at);
}

[data-direction="rtl"]:has(> .wm-sidebar) {
  flex-direction: row-reverse;
}

/* HTML Layout testing */
:has(> .wm-sidebar) > :not(:nth-child(1), :nth-child(2)) {
  outline: var(--error-outline);
  --error: "Sidebar layouts must include exactly two child elements.";
}
/* ---------------- END OF SIDEBAR ---------------- */

/** 
** SWITCHER
*
* A layout that allows to switch a Flexbox content between a horizontal
* and a vertical layout at a given, container-based breakpoint. Thus
* bypassing any intermediate layout states.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*
* --switcher-column-gap (1rem): This defines horizontal space
* between child elements.
*
* --switcher-row-gap (1rem): This defines vertical space
* between child elements.
*
* --switcher-target-container-width (40rem): The containing component's width at
* which it switches between a horizontal and vertical layout.
*/
.wm-switcher {
  --switcher-target-container-width: 40rem;

  display: flex;
  align-items: var(--switcher-vertical-alignment, flex-start);
  flex-wrap: wrap;
  column-gap: var(--switcher-column-gap, var(--gutter, var(--space-s-l)));
  row-gap: var(--swithcer-row-gap, var(--gutter, var(--space-s-l)));
}

.wm-switcher > * {
  flex-grow: 1;
  /* 
    The flex-basis value enters the (current) width of the container,
    expressed as 100%, into a calculation with the designated 20rem 
    breakpoint. Depending on the parsed value of 100%, this will return
    either a positive or negative value: positive if the container
    is narrower than the breakpoint, or negative if it is wider.
    
    This number is then multiplied by 999 to produce either a very 
    large positive number or a very large negative number.

    A negative flex-basis value is invalid, and dropped. This means just
    the flex-basis line is ignored, and the rest of the CSS is still 
    applied. The erroneous negative flex-basis value is corrected to 0 
    and—because flex-grow is present — each element grows to take up an 
    equal proportion of the horizontal space.

    If, on the other hand, the calculated flex-basis value is a large 
    positive number, each element maxes out to take up a whole row. 
    This results in the vertical configuration. Intermediary 
    configurations are successfully bypassed.
  */
  flex-basis: calc((var(--switcher-target-container-width) - 100%) * 999);

  /*
    The previous statement, "each element grows to take up an equal 
    proportion of the horizontal space" is true where the content 
    of any one element does not exceed that allotted proportion.

    To keep things in order, nested elements can be given a 
    max-inline-size of 100%.
  */
  /* max-inline-size: 100%; */
}

/* The maximum number of elements allowed to appear in the horizontal */
/* configuration. Default set to 2 elements */
.wm-switcher > :nth-last-child(n + 3),
.wm-switcher > :nth-last-child(n + 3) ~ * {
  flex-basis: 100%;
}
/* ---------------- END OF SWITCHER ---------------- */

/** 
** COVER
*
* A layout that allows to handle vertically centered content (under a
* min-block-size threshold) and can acommodate top/header and 
* bottom/footer elements.
*
* The COVER has one principal element that should always gravitate towards
* the center. In addition, it can optionally have one top/header element 
* and/or one bottom/footer element. 
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*/
.wm-cover {
  display: flex;
  flex-direction: column;
  min-block-size: var(--cover-min-height, 100vh);
  padding: var(--cover-space, var(--gutter, var(--space-s-m)));
}

.cover > * {
  margin-block: var(--gutter, var(--space-s-m));
}

.cover > :first-child:not(.principal-element) {
  margin-block-start: 0;
}

.cover > :last-child:not(.principal-element) {
  margin-block-end: 0;
}

.cover > .principal-element {
  margin-block: auto;
}
/* ---------------- END OF COVER ---------------- */

/** 
** GRID
*
* A layout that allows to 
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*
* EXCEPTIONS:
*
* [data-layout="halves"]: two column grid
*
* [data-layout="thirds"]: three column grid
*/
.wm-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-placement, auto-fill), minmax(var(--grid-min-item-size, 16rem), 1fr));
  column-gap: var(--grid-column-gap, var(--gutter, var(--space-s-m)));
  row-gap: var(--grid-row-gap, var(--gutter, var(--space-s-m)));
}

.wm-grid[data-layout="halves"] {
  --grid-placement: auto-fit;
  --grid-min-item-size: clamp(16rem, 50vw, 33rem);
}

.wm-grid[data-layout="thirds"] {
  --grid-placement: auto-fit;
  --grid-min-item-size: clamp(16rem, 33%, 20rem);
}
/* ---------------- END OF GRID ---------------- */

/** 
** FRAME
*
* A layout that allows to
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*/
.wm-frame {
  aspect-ratio: var(--frame-numerator, 16) / var(--frame-denominator, 9);
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
}

.wm-frame > img,
.wm-frame > video {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

@media (orientation: portrait) {
  .wm-frame {
    --frame-numerator: 1;
    --frame-denominator: 1;
  }
}
/* ---------------- END OF FRAME ---------------- */

/** 
** REEL
*
* A layout that allows to
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*/
.wm-reel {
  --reel-item-width: calc(33.333% - var(--reel-calculated-space, var(--reel-space, var(--gutter, 2rem))));
  --scroll-bar-size: 10px;
  --scroll-bar-track-color: initial;
  --scroll-bar-thumb-color: initial;

  display: flex;
  max-width: 100%;
  height: auto;
  margin-inline: auto;
  padding: 0 0 var(--scroll-bar-offset, 0) 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.wm-reel:focus-visible {
  outline: 0;
}

.wm-reel::-webkit-scrollbar {
  block-size: var(--scroll-bar-size);
}

.wm-reel::-webkit-scrollbar-track {
  background-color: var(--scroll-bar-track-color);
}

.wm-reel::-webkit-scrollbar-thumb {
  background-color: var(--scroll-bar-thumb-color);
}

.wm-reel > * {
  flex-basis: var(--reel-item-width);
  flex-shrink: 0;
}

/* 
Spacing between reel items.

Since the Reel's content is not desgined to wrap, we shall use the
margin-based solution instead of 'gap' property. It's better supported.
*/
.wm-reel > * + * {
  margin-inline-start: var(--reel-calculated-space);
}

/* 
For images, which may be very large or use differing aspect ratios, 
one may want to set the Reel's height. 

The commom image 'block-size' (height in horizontal-tb writing mode) 
should accordingly be 100%, ahd the 'width' auto. This will ensure 
the images share a height but maintain their own aspect ratio.
*/
.wm-reel > img {
  block-size: 100%;
  width: auto;
}

.wm-reel[data-scroll="snap"] {
  scroll-snap-type: x mandatory;
}

.wm-reel[data-scroll="snap"] > * {
  scroll-snap-align: start;
}
/* ---------------- END OF REEL ---------------- */

/** 
** IMPOSTER
*
* A layout that allows to add a general purpose superimposition 
* element for centrally positioning it over the viewport,
* the document, or a selected "positioning container" element.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*/
.wm-imposter {
  /* ↓ Position the top left corner in the center */
  position: var(--imposter-positioning, absolute);
  /* ↓ equivalent to `top` in horizontal-tb writing mode */
  inset-block-start: 50%;
  /* ↓ equivalent to `left` in horizontal-tb writing mode */
  inset-inline-start: 50%;
  /* ↓ Reposition so the center of the element
  is the center of the positioning container */
  transform: translate(-50%, -50%);
}

/* 
Imposter variant that is contained within its positioning
container. Constricted contens can be scrolled into view.

Also set up an option for adding a minimum gap between 
the Imposter element and the inside edges of its 
positiong container.
*/
.wm-imposter[data-imposter-variant="contain"] {
  overflow: auto;
  /* ↓ equivalent to `max-width` in horizontal-tb writing mode */
  max-inline-size: calc(100% - (var(--imposter-space, 1rem) * 2));
  /* ↓ equivalent to `max-height` in horizontal-tb writing mode */
  max-block-size: calc(100% - (var(--imposter-space, 1rem) * 2));
}
/* ---------------- END OF IMPOSTER ---------------- */

/** 
** ICON
*
* A layout that allows to treat icons like text, and get them
* to accompany text as seamlessly as possible. 
*
* SVG is the preferred format for icons. One major advantage is the
* to use 'currentColor' keyword, which makes the SVG inherit the 
* surronding text color automatically.
*
* By default, SVGs align to the text's baseline - behaving like a
* a typographic character. This makes them naturally integrate with 
* text flows.
*
* Icons are typically injected using the '<use>' element, referencing
* one of many '<symbol>' definitions in a single external 'icons.svg'
* file - minimizing HTTP requests. The `currentColor` technique 
* continues to work with this approach.
*
* For unusually tall icons, you can adjust vertical alignment using
* the `vertical-align` property with a length. This length represents 
* the distance above the baseline, and can take a negative value.
*
* For our Icon layout, we shal stick to sitting icons on the baseline.
* This is the most robust approach since icons that hang below the 
* baseline may collide with a successive line of text where wrapping
* occurs.
*
* Default icon dimensions may be too small depending on the font size
* used in a given component. These values are intended as fallbacks to
* prevent icons from collapsing entirely (if no intrinsic dimensions
* were provided), not as final sizing rules. The expectation is that 
* icons will inherit or be adjusted contextually based on where they 
* are used.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*/

/*
Matching icon height to text

A suitable icon height, starting at the baseline, depends somewhat on 
the casing of the font and the presence or absence of descenders.

Depending on the project the value should be adjusted but usually 
'0.75em' is more closely matched uppercase letter height.

As progressive enhancement the emerging 'cap' unit promises to
evaluate the individual front for a more accurate match.

One could define custom properties for defining icon dimensions.
But with current setup icon scales automatically with the text
'font-size'.
 */
.wm-icon {
  width: var(--icon-size, 0.75em);
  width: var(--icon-size-cap, 1cap);
  height: var(--icon-size, 0.75em);
  height: var(--icon-size-cap, 1cap);
}

/* 
If icon text is to be lowercase, it is better to match the icon
height to a lowercase letter. This is already possible using 'ex'\
unit which pertains to the height of a lowercase "x".
*/
.wm-icon[data-icon-variant="lowercase"] {
  width: var(--icon-size-lowercase, 1ex);
  height: var(--icon-size-lowercase, 1ex);
}

/* 
If we need to control space between icon and an element (e.g. text) 
we can apply a containing block with 'with-icon' class.

We use class-like selector by matching class values that begin with
'with-icon'. As on project basis we might add other varitions of icon
containig blocks (e.g. with icon link component that contains only
visible icon and hidden label for accessibility purpose.
*/
[class^="wm-with-icon"] {
  display: var(--with-icon-display, inline-flex);
  flex-direction: var(--with-icon-direction, row);
  justify-content: var(--with-icon-horizontal-alignment, center);
  align-items: var(--with-icon-vertical-alignment, baseline);
  gap: var(--with-icon-gutter, 0.5em);
}

/* 
Exceptions as "reversed" and "hidden-label" are self-excluded as 
won't be applied together at the same time.
*/
[class^="wm-with-icon"][data-with-icon-variant="reversed"] {
  --with-icon-direction: row-reverse;
}

[class^="wm-with-icon"][data-with-icon-variant="hidden-label"] {
  --with-icon-horizontal-alignment: center;
  --with-icon-vertical-alignment: center;

  /* 
  ↓ Web accessibility. 
  See https://www.smashingmagazine.com/2023/04/accessible-tap-target-sizes-rage-taps-clicks/ 
  */
  min-height: var(--with-icon-hidden-label-min-tap-size, 44px);
  min-width: var(--with-icon-hidden-label-min-tap-size, 44px);
}
/* ---------------- END OF ICON ---------------- */

/** 
** CONTAINER
*
* A meta-layout utility that allows to establish containers
* to finese the other layouts in ways that would not otherwise
* be possible.
*
* CUSTOM PROPERTIES AND CONFIGURATION:
*/
.wm-container {
  container-name: var(--container-name, wmContainer);
  container-type: inline-size;
}
/* ---------------- END OF CONTAINER ---------------- */
