diff --git a/core/src/themes-migrated/ionic.functions.color.scss b/core/src/themes-migrated/ionic.functions.color.scss new file mode 100644 index 0000000000..1d78bbe131 --- /dev/null +++ b/core/src/themes-migrated/ionic.functions.color.scss @@ -0,0 +1,59 @@ +// Gets the active color's css variable from a variation. Alpha is optional. +// -------------------------------------------------------------------------------------------- +// Example usage: +// current-color(base) => var(--ion-color-base) +// current-color(contrast, 0.1) => rgba(var(--ion-color-contrast-rgb), 0.1) +// -------------------------------------------------------------------------------------------- +@function current-color($variation, $alpha: null) { + @if $alpha == null { + @return var(--ion-color-#{$variation}); + } @else { + @return rgba(var(--ion-color-#{$variation}-rgb), #{$alpha}); + } +} + +// Gets the specific color's css variable from the name and variation. Alpha/rgb are optional. +// -------------------------------------------------------------------------------------------- +// Example usage: +// ion-color(primary, base) => var(--ion-color-primary, #3880ff) +// ion-color(secondary, contrast) => var(--ion-color-secondary-contrast) +// ion-color(primary, base, 0.5) => rgba(var(--ion-color-primary-rgb, 56, 128, 255), 0.5) +// -------------------------------------------------------------------------------------------- +@function ion-color($name, $variation, $alpha: null, $rgb: null) { + $values: map-get($colors, $name); + $value: map-get($values, $variation); + $variable: --ion-color-#{$name}-#{$variation}; + + @if ($variation == base) { + $variable: --ion-color-#{$name}; + } + + @if ($alpha) { + $value: color-to-rgb-list($value); + @return rgba(var(#{$variable}-rgb, $value), $alpha); + } + @if ($rgb) { + $value: color-to-rgb-list($value); + $variable: #{$variable}-rgb; + } + + @return var(#{$variable}, $value); +} + +// Mixes a color with black to create its shade. +// -------------------------------------------------------------------------------------------- +@function get-color-shade($color) { + @return mix(#000, $color, 12%); +} + +// Mixes a color with white to create its tint. +// -------------------------------------------------------------------------------------------- +@function get-color-tint($color) { + @return mix(#fff, $color, 10%); +} + +// Converts a color to a comma separated rgb. +// -------------------------------------------------------------------------------------------- +@function color-to-rgb-list($color) { + @return #{red($color)},#{green($color)},#{blue($color)}; +} diff --git a/core/src/themes-migrated/ionic.functions.string.scss b/core/src/themes-migrated/ionic.functions.string.scss new file mode 100644 index 0000000000..098a2e9f73 --- /dev/null +++ b/core/src/themes-migrated/ionic.functions.string.scss @@ -0,0 +1,178 @@ +// String Utility Functions +// -------------------------------------------------------------------------------- + +// String Replace Function +// -------------------------------------------------------------------------------- + +@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; +} + +// String Split Function +// -------------------------------------------------------------------------------- + + +@function str-split($string, $separator) { + // empty array/list + $split-arr: (); + // first index of separator in string + $index: str-index($string, $separator); + // loop through string + @while $index != null { + // get the substring from the first character to the separator + $item: str-slice($string, 1, $index - 1); + // push item to array + $split-arr: append($split-arr, $item); + // remove item and separator from string + $string: str-slice($string, $index + 1); + // find new index of separator + $index: str-index($string, $separator); + } + // add the remaining string to list (the last item) + $split-arr: append($split-arr, $string); + + @return $split-arr; +} + + +// String Extract Function +// -------------------------------------------------------------------------------- + +@function str-extract($string, $start, $end) { + $start-index: str-index($string, $start); + + @if $start-index { + $post: str-slice($string, $start-index + str-length($start)); + $end-index: str-index($post, $end); + + @if $end-index { + @return str-slice($post, 1, $end-index - 1); + } + } + + @return null; +} + + +// String Contains Function +// -------------------------------------------------------------------------------- + +@function str-contains($string, $needle) { + @if (type-of($string) == string) { + @return str-index($string, $needle) != null; + } + + @return false; +} + + +// URL Encode Function +// -------------------------------------------------------------------------------- + +@function url-encode($val) { + $spaces: str-replace($val, " ", "%20"); + $encoded: str-replace($spaces, "#", "%23"); + @return $encoded; +} + + +// Add Root Selector +// -------------------------------------------------------------------------------- +// Adds a root selector using host based on the selector passed +// -------------------------------------------------------------------------------- + +@function add-root-selector($root, $addHostSelector) { + $selectors: str-split($root, ","); + + $list: (); + + @each $selector in $selectors { + // If the selector contains :host( it means it is targeting a class on the host + // element so we need to change how we target it: + // @include add-root-selector(":host(.fixed)", "[dir=rtl]") + // --> :host-context([dir=rtl]):host(.fixed) + // --> :host-context([dir=rtl]).fixed + @if str-contains($selector, ":host(") { + // @include add-root-selector(":host(.fixed)", "[dir=rtl]") + // --> :host-context([dir=rtl]):host(.fixed) + $shadow-element: str-replace($selector, ":host(", ":host-context(#{$addHostSelector}):host("); + $list: append($list, $shadow-element, comma); + + $new-element: (); + $elements: str-split($selector, " "); + + @each $element in $elements { + @if str-contains($element, ":host(") { + $scoped-element: $element; + + // Replace the :host( and ) so all we have left is the class + // inside of it: + // :host(.fixed) -> .fixed + $scoped-element: str-replace($scoped-element, ")", ""); + $scoped-element: str-replace($scoped-element, ":host(", ""); + + // Add the class back inside of host with the rtl selector: + // .fixed -> :host-context([dir=rtl]).fixed + $scoped-element: str-replace($scoped-element, $scoped-element, ":host-context(#{$addHostSelector})#{$scoped-element}"); + + // @include add-root-selector(":host(.fixed)", "[dir=rtl]") + // --> :host-context([dir=rtl]).fixed + $new-element: append($new-element, $scoped-element, space); + } @else { + // Add back any selectors that followed the host after transforming the + // first selector: + // :host(.fixed) ::slotted(ion-icon) + // --> :host-context([dir=rtl]):host(.fixed) ::slotted(ion-icon) + // --> :host-context([dir=rtl]).fixed ::slotted(ion-icon) + $new-element: append($new-element, $element, space); + } + } + + $list: append($list, $new-element, comma); + // If the selector contains :host without a parantheses + // it means it is targeting just the host + // element so we can change it to look for host-context + // @include add-root-selector(":host", "[dir=rtl]") + // --> :host-context([dir=rtl]) + // --> :host:dir(rtl) + } @else if str-contains($selector, ":host") { + $new-element: (); + $elements: str-split($selector, " "); + + @each $element in $elements { + @if str-contains($element, ":host") { + // Replace the :host with the addHostSelector: + // :host -> :host-context([dir=rtl]) + $updated-element: str-replace($element, ":host", ":host-context(#{$addHostSelector})"); + + // Add the final selector after all transformations: + // :host -> :host-context([dir=rtl]) + $new-element: append($new-element, $updated-element, space); + } @else { + // Add back any selectors that followed the host after transforming the + // first selector: + // :host ::slotted(ion-icon) -> :host-context([dir=rtl]) ::slotted(ion-icon) + $new-element: append($new-element, $element, space); + } + } + + $list: append($list, $new-element, comma); + // If the selector does not contain host at all it is either a shadow + // or normal element so append both the addHostSelector and host-context + // @include add-root-selector("ion-component", "[dir=rtl]") + // --> :host-context([dir=rtl]) ion-component + // --> [dir=rtl] ion-component + } @else { + $list: append($list, "#{$addHostSelector} #{$selector}", comma); + $list: append($list, ":host-context(#{$addHostSelector}) #{$selector}", comma); + } + } + + @return $list; +} diff --git a/core/src/themes-migrated/ionic.globals.ios.scss b/core/src/themes-migrated/ionic.globals.ios.scss new file mode 100644 index 0000000000..d9aca20c4f --- /dev/null +++ b/core/src/themes-migrated/ionic.globals.ios.scss @@ -0,0 +1,6 @@ + +// Global Core +@import "./ionic.globals"; + +// Global iOS +@import "./ionic.theme.default.ios"; diff --git a/core/src/themes-migrated/ionic.globals.md.scss b/core/src/themes-migrated/ionic.globals.md.scss new file mode 100644 index 0000000000..6c0d558d01 --- /dev/null +++ b/core/src/themes-migrated/ionic.globals.md.scss @@ -0,0 +1,6 @@ + +// Core Globals +@import "./ionic.globals"; + +// Material Design Globals +@import "./ionic.theme.default.md"; diff --git a/core/src/themes-migrated/ionic.globals.scss b/core/src/themes-migrated/ionic.globals.scss new file mode 100644 index 0000000000..e1495fabd0 --- /dev/null +++ b/core/src/themes-migrated/ionic.globals.scss @@ -0,0 +1,67 @@ + +// Global Utility Functions +@import "./ionic.functions.string"; + +// Global Color Functions +@import "./ionic.functions.color"; + +// Global Mixins +@import "./ionic.mixins"; + +// Default Theme +@import "./ionic.theme.default"; + + +// Default General +// -------------------------------------------------- +$font-family-base: var(--ion-font-family, inherit) !default; + +// Global app direction +$app-direction: null !default; + +// Hairlines width +$hairlines-width: .55px !default; + +// The minimum dimensions at which your layout will change, +// adapting to different screen sizes, for use in media queries +$screen-breakpoints: ( + xs: 0, + sm: 576px, + md: 768px, + lg: 992px, + xl: 1200px +) !default; + +// Input placeholder opacity +// Ensures that the placeholder has the +// correct color contrast against the background. +$placeholder-opacity: 0.6 !default; + +$form-control-label-margin: 16px !default; + + +// Z-Index +// -------------------------------------------------- +// Grouped by elements which would be siblings + +$z-index-menu-overlay: 1000; +$z-index-overlay: 1001; +$z-index-click-block: 99999; + +$z-index-fixed-content: 999; +$z-index-scroll-content: 1; +$z-index-refresher: -1; + +$z-index-page-container: 0; +$z-index-toolbar: 10; +$z-index-toolbar-background: -1; +$z-index-toolbar-buttons: 99; + +$z-index-backdrop: 2; +$z-index-overlay-wrapper: 10; + +$z-index-item-options: 1; +$z-index-item-input: 2; +$z-index-item-divider: 100; + +$z-index-reorder-selected: 100; diff --git a/core/src/themes-migrated/ionic.mixins.scss b/core/src/themes-migrated/ionic.mixins.scss new file mode 100644 index 0000000000..053a4627c6 --- /dev/null +++ b/core/src/themes-migrated/ionic.mixins.scss @@ -0,0 +1,593 @@ +@mixin input-cover() { + @include position(0, null, null, 0); + @include margin(0); + + position: absolute; + + width: 100%; + height: 100%; + + border: 0; + background: transparent; + cursor: pointer; + + appearance: none; + outline: none; + + &::-moz-focus-inner { + border: 0; + } +} + +@mixin visually-hidden() { + position: absolute; + + top: 0; + left: 0; + right: 0; + bottom: 0; + + width: 100%; + height: 100%; + + margin: 0; + padding: 0; + + border: 0; + outline: 0; + clip: rect(0 0 0 0); + + opacity: 0; + overflow: hidden; + + -webkit-appearance: none; + -moz-appearance: none; +} + +@mixin text-inherit() { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + letter-spacing: inherit; + text-decoration: inherit; + text-indent: inherit; + text-overflow: inherit; + text-transform: inherit; + text-align: inherit; + white-space: inherit; + color: inherit; +} + +@mixin button-state() { + @include position(0, 0, 0, 0); + + position: absolute; + + content: ""; + + opacity: 0; +} + +// Font smoothing +// -------------------------------------------------- + +@mixin font-smoothing() { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +// Get the key from a map based on the index +@function index-to-key($map, $index) { + $keys: map-keys($map); + + @return nth($keys, $index); +} + + +// Breakpoint Mixins +// --------------------------------------------------------------------------------- + +// Breakpoint viewport sizes and media queries. +// +// Breakpoints are defined as a map of (name: minimum width), order from small to large: +// +// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px) +// +// The map defined in the `$screen-breakpoints` global variable is used as the `$breakpoints` argument by default. + +// --------------------------------------------------------------------------------- + +// Minimum breakpoint width. Null for the smallest (first) breakpoint. +// +// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// 576px +@function breakpoint-min($name, $breakpoints: $screen-breakpoints) { + $min: map-get($breakpoints, $name); + + @return if($name != index-to-key($breakpoints, 1), $min, null); +} + +// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront. +// Useful for making responsive utilities. +// +// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// "" (Returns a blank string) +// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// "-sm" +@function breakpoint-infix($name, $breakpoints: $screen-breakpoints) { + @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); +} + +// Media of at least the minimum breakpoint width. No query for the smallest breakpoint. +// Makes the @content apply to the given breakpoint and wider. +@mixin media-breakpoint-up($name, $breakpoints: $screen-breakpoints) { + $min: breakpoint-min($name, $breakpoints); + @if $min { + @media (min-width: $min) { + @content; + } + } @else { + @content; + } +} + +// Name of the next breakpoint, or null for the last breakpoint. +// +// >> breakpoint-next(sm) +// md +// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// md +// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl)) +// md +@function breakpoint-next($name, $breakpoints: $screen-breakpoints, $breakpoint-names: map-keys($breakpoints)) { + $n: index($breakpoint-names, $name); + @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); +} + +// Maximum breakpoint width. Null for the smallest (first) breakpoint. +// The maximum value is reduced by 0.02px to work around the limitations of +// `min-` and `max-` prefixes and viewports with fractional widths. +// +// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max +// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. // Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. +// See https://bugs.webkit.org/show_bug.cgi?id=178261 // See https://bugs.webkit.org/show_bug.cgi?id=178261 +// +// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// 767.98px +@function breakpoint-max($name, $breakpoints: $screen-breakpoints) { + $max: map-get($breakpoints, $name); + @return if($max and $max > 0, $max - .02, null); +} + +// Media of at most the maximum breakpoint width. No query for the largest breakpoint. +// Makes the @content apply to the given breakpoint and narrower. +@mixin media-breakpoint-down($name, $breakpoints: $screen-breakpoints) { + $max: breakpoint-max($name, $breakpoints); + @if $max { + @media (max-width: $max) { + @content; + } + } @else { + @content; + } +} + + +// Text Direction - ltr / rtl +// +// CSS defaults to use the ltr css, and adds [dir=rtl] selectors +// to override ltr defaults. +// ---------------------------------------------------------- + +@mixin multi-dir() { + @content; + + // $root: #{&}; + // @at-root [dir] { + // #{$root} { + // @content; + // } + // } +} + +@mixin rtl() { + $root: #{&}; + + $rootSplit: str-split($root, ","); + $selectors: #{add-root-selector($root, "[dir=rtl]")}; + $selectorsSplit: str-split($selectors, ","); + + $hostContextSelectors: (); + $restSelectors: (); + $dirSelectors: (); + + // Selectors must be split into individual selectors in case the browser + // doesn't support a specific selector. + // For example, Firefox and Safari doesn't support `:host-context()`. + // If an invalid selector is used, then the entire group of selectors + // will be ignored. + // @link https://www.w3.org/TR/selectors-3/#grouping + @each $selector in $selectorsSplit { + // Group the selectors back into a single selector to optimize the output. + @if str-index($selector, ":host-context") { + $hostContextSelectors: append($hostContextSelectors, $selector, comma); + } @else { + // Group the selectors back into a single selector to optimize the output. + $restSelectors: append($restSelectors, $selector, comma); + } + } + + // Supported by Chrome. + @if length($hostContextSelectors) > 0 { + @at-root #{$hostContextSelectors} { + @content; + } + } + + // Supported by all browsers. + @if length($restSelectors) > 0 { + @at-root #{$restSelectors} { + @content; + } + } + + // If browser can support `:dir()`, then add the `:dir()` selectors. + @supports selector(:dir(rtl)) { + // Adding :dir() in case the browser doesn't support `:host-context()` and does support `:dir()`. + // `:host-context()` is added: + // - through the `add-root-selector()` function. + // - first so that it takes precedence over `:dir()`. + // For example, + // - Firefox doesn't support `:host-context()`, but does support `:dir()`. + // - Safari doesn't support `:host-context()`, but Safari 16.4+ supports `:dir()` + // @link https://webkit.org/blog/13966/webkit-features-in-safari-16-4/ + @each $selector in $rootSplit { + $dirSelector: "#{$selector}:dir(rtl)"; + // Group the selectors back into a single selector to optimize the output. + $dirSelectors: append($dirSelectors, $dirSelector, comma); + } + + // Supported by Firefox. + @if length($dirSelectors) > 0 { + @at-root #{$dirSelectors} { + @content; + } + } + } +} + +@mixin ltr() { + @content; +} + + +// SVG Background Image Mixin +// @param {string} $svg +// ---------------------------------------------------------- +@mixin svg-background-image($svg, $flip-rtl: false) { + $url: url-encode($svg); + $viewBox: str-split(str-extract($svg, "viewBox='", "'"), " "); + + @if $flip-rtl != true or $viewBox == null { + @include multi-dir() { + background-image: url("data:image/svg+xml;charset=utf-8,#{$url}"); + } + } @else { + $transform: "transform='translate(#{nth($viewBox, 3)}, 0) scale(-1, 1)'"; + $flipped-url: $svg; + $flipped-url: str-replace($flipped-url, " + + + + CSS Variable - Basic + + + + + + + + + + + + + + + + + + Lists + + + + Colors + + + + Overlays + + + + + + + + + + + Default + Solid + Clear + Outline + + + Default Buttons + + + + + Default + Solid + Clear + Outline + + + + + + + + Default + Solid + Clear + Outline + + + Focused Buttons + + + + + Default + Solid + Clear + Outline + + + + + + + + Default + Solid + Clear + Outline + + + Activated Buttons + + + + + Default + Solid + Clear + Outline + + + + + + + Default + + + Oceanic + + + Vibrant + + + Dark + + + Toolbar + + + Disabled + + + + + + + + + + Home + + + + Documents + + + + Folders + + + + Files + + + + Projects + + + + User Research + + + + Survey.txt + + + +
+
+ + + Card Header + + + + Keep close to Nature's heart... and break clear away, once in awhile, and climb a mountain or spend + a week in the woods. Wash your spirit clean. + + + + + + ion-item in a card, icon left, button right + View + + + + This is content, without any paragraph or header tags, within an ion-card-content element. + + + + + + + Card Link Item 1 activated + + + + + Card Link Item 2 + + + + + Card Button Item 1 activated + + + + + Card Button Item 2 focused + + + + + + + + Standard + + Error Text + + + + Standard + + + Helper Text + + + +
+ +
+ + + Pokémon Yellow + Note + + + Super Metroid + Note + + + Mega Man X + Note + + + The Legend of Zelda + Note + + + Pac-Man + Note + + + +

Super Mario World

+

Featuring Luigi

+
+ Note +
+ + +

Street Fighter II

+

The Sequel

+
+ Note +
+ Sliding Items + + + Half Life + + + More + + + + + Final Fantasy VII + + + More + + + + + Tetris + + + More + + + + + Donkey Kong III + + + More + + + + + Goldeneye 007 + + + More + + +
+
+ +
+ + + + Fruits + + + Apple + + + + + Grape, checked, disabled + + + + + Cherry + + + + + + + + + Veggies + + + Celery + + + + + Lettuce, checked, disabled + + + + + Onion + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + Default + + + + Primary + + + + Secondary + + + + Warning + + + + Danger + + + + Light + + + + Dark + + + + Unchecked by Default + + + + Disabled + + + + + + Blueberry + + + + + Lettuce + + + + + Apple + + + + + Radish + + + + + Blackberry + + +
+
+
+
+ + + + + Colors +
+ Select a Theme: + + Default + Oceanic + Vibrant + Dark + +
+
+
+ + +
+
+

+ Primary + Secondary + Tertiary + Success Outline + Warning Clear + Danger + Light + Dark + Medium Outline + Dark Clear +

+ +

+ Primary + Secondary + Tertiary + Success Outline + Warning Clear + Danger + Light + Dark + Medium Outline + Dark Clear +

+ +

+ Primary + Secondary + Tertiary + Success Outline + Warning Clear + Danger + Light + Medium Outline + Dark Clear +

+
+ +
+

+ Default + + + + Map + Round + Outline + Clear + Full Outline + Block Clear +

+ +

+ + + + + + + + + + + + +

+ +

+ + Default + + + Primary + + + Secondary + + + Tertiary + + + Success + + + Warning + + + Danger + + + Light + + + Medium + + + Dark + +

+ +

+ + 11 + 22 + 33 + 44 + 55 + 66 + 77 + 88 + 99 +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +

+ + Home + Electronics + Photography + Cameras + Film + 35 mm + + + + Home + Electronics + Photography + Cameras + Film + 35 mm + + + + Home + Electronics + Photography + Cameras + Film + 35 mm + + + + Home + Electronics + Photography + Cameras + Film + 35 mm + +

+
+ +
+

+ + + + + + +

+

+ + + All + + + Favorites + + + + + + All + + + Favorites + + + + + + All + + + Favorites + + + + + + All + + + Favorites + + + + + + All + + + Favorites + + + + + + All + + + Favorites + + +

+
+ +
+

+ + + + + + + + + + + + Toolbar + + + + + + + + + + + + + + + All + Favorites + + + + + + + + + + + + + + + + + + + + + + + + Tertiary + + + + + + + + + + + + + + Success + + + + + + + + + + + + + + Warning + + + + + + + + + + + + + + + + + + + + + + + + All + Favorites + + + + + + + + + + + + + + + Medium + + + + + + + + + + + + + + Dark + +

+ +

+ + + + + + + + + +

+
+
+
+
+ + + + + Overlays +
+ Select a Theme: + + Default + Oceanic + Vibrant + Dark + +
+
+
+ +
+ + + + Present Alert + + + Present Action Sheet + + + Present Loading + + + Present Modal + + + Present Popover + + + Present Toast + + + Datetime + + + + Select + + 1 + 2 + 3 + + + + +
+
+
+
+
+ + + + diff --git a/core/src/themes-migrated/test/steps/index.html b/core/src/themes-migrated/test/steps/index.html new file mode 100644 index 0000000000..3579469b36 --- /dev/null +++ b/core/src/themes-migrated/test/steps/index.html @@ -0,0 +1,225 @@ + + + + + Themes - Steps + + + + + + + + + + + + + +
Background
+
Step 50
+
Step 100
+
Step 150
+
Step 200
+
Step 250
+
Step 300
+
Step 350
+
Step 400
+
Step 450
+
Step 500
+
Step 550
+
Step 600
+
Step 650
+
Step 700
+
Step 750
+
Step 800
+
Step 850
+
Step 900
+
Step 950
+
+ +
Text
+
Step 50
+
Step 100
+
Step 150
+
Step 200
+
Step 250
+
Step 300
+
Step 350
+
Step 400
+
Step 450
+
Step 500
+
Step 550
+
Step 600
+
Step 650
+
Step 700
+
Step 750
+
Step 800
+
Step 850
+
Step 900
+
Step 950
+
+
+ +