maverick-wave 5.12.0 → 5.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/.claude/skills/mw-maverick-wave/SKILL.md +63 -8
  2. package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +6 -3
  3. package/.claude/skills/mw-maverick-wave/references/components.md +41 -27
  4. package/.claude/skills/mw-maverick-wave/references/javascript.md +27 -26
  5. package/.claude/skills/mw-maverick-wave/references/layout.md +12 -6
  6. package/.claude/skills/mw-maverick-wave/references/theming.md +59 -4
  7. package/CHANGELOG.md +12 -0
  8. package/README.md +45 -5
  9. package/index.html +5 -4
  10. package/maverick-wave.min.css +4 -4
  11. package/maverick-wave.min.js +1 -1
  12. package/package.json +2 -2
  13. package/src/js/main.js +67 -20
  14. package/src/partials/buttons-container.html +2 -1
  15. package/src/partials/get-started-container.html +52 -3
  16. package/src/partials/parallax-container.html +8 -5
  17. package/src/partials/tabs-container.html +1 -1
  18. package/src/partials/typography-container.html +3 -0
  19. package/src/scss/abstracts/_functions.scss +50 -0
  20. package/src/scss/abstracts/_mixins.scss +33 -0
  21. package/src/scss/base/_typography.scss +4 -21
  22. package/src/scss/components/_buttons.scss +1 -1
  23. package/src/scss/components/_content-slider.scss +16 -8
  24. package/src/scss/components/_dropdown.scss +1 -1
  25. package/src/scss/components/_gallery.scss +13 -2
  26. package/src/scss/components/_lang-switch.scss +1 -1
  27. package/src/scss/components/_segmented.scss +4 -0
  28. package/src/scss/layout/_header.scss +3 -2
  29. package/src/scss/layout/_parallax.scss +15 -1
  30. package/src/scss/layout/_section.scss +2 -10
  31. package/src/scss/utilities/_touch-targets.scss +22 -16
@@ -1 +1 @@
1
- !function(){"use strict";function e(e,t){e&&(e.classList.toggle("mw-active",t),t||e.classList.remove("active"))}function t(e){return!!e&&(e.classList.contains("mw-active")||e.classList.contains("active"))}function n(){const e=Array.from(document.querySelectorAll(".color-swatch")),t=e.map(e=>function(e){if(!e||"transparent"===e)return"#000000";const t=e=>Math.max(0,Math.min(255,Math.round(e))).toString(16).padStart(2,"0"),n=e.match(/color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\)/);if(n){const e="#"+t(255*n[1])+t(255*n[2])+t(255*n[3]),a=void 0===n[4]?1:parseFloat(n[4]);return(a<1?`${e} / ${a}`:e).toUpperCase()}const a=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(!a)return e;const o="#"+t(a[1])+t(a[2])+t(a[3]),r=void 0===a[4]?1:parseFloat(a[4]);return(r<1?`${o} / ${r}`:o).toUpperCase()}(window.getComputedStyle(e).backgroundColor));e.forEach((e,n)=>{const a=e.parentElement.querySelector(".mw-text-muted");a&&(a.textContent=t[n])})}function a(){const e=parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop);return Number.isNaN(e)?0:e}document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".mw-gallery").forEach(e=>{const t=e.closest(".mw-gallery-container")||e.parentElement;t&&function(e,t){const n=t.querySelector(".mw-gallery-track");if(!n||!n.children.length)return;const a=n.children,o=e.querySelector(".mw-gallery-dots"),r=e.querySelector(".mw-gallery-desc"),c=t.querySelector(".mw-gallery-navi-prev"),s=t.querySelector(".mw-gallery-navi-next");let l=0;function i(e){const t=a.length;l=(e+t)%t,n.style.transform=`translateX(-${100*l}%)`,o&&o.querySelectorAll(".mw-gallery-dot").forEach((e,t)=>{e.classList.toggle("mw-active",t===l)}),r&&(r.textContent=a[l].dataset.desc||"")}if(o)for(let e=0;e<a.length;e++){const t=document.createElement("button");t.type="button",t.className="mw-gallery-dot"+(0===e?" mw-active":""),t.setAttribute("aria-label","Go to slide "+(e+1)),t.addEventListener("click",()=>i(e)),o.appendChild(t)}c&&c.addEventListener("click",()=>i(l-1)),s&&s.addEventListener("click",()=>i(l+1));let d=0;n.addEventListener("touchstart",e=>d=e.touches[0].clientX),n.addEventListener("touchend",e=>{const t=e.changedTouches[0].clientX-d;t>50&&i(l-1),t<-50&&i(l+1)}),i(0)}(t,e)}),function(){const t=document.querySelector(".mw-theme-toggle");if(!t)return;const a=document.body,o=document.documentElement,r=t.querySelector(".mw-theme-toggle-slider i"),c=getComputedStyle(document.documentElement).getPropertyValue("--mw-internal-theme-mode").trim()||"switchable";if("switchable"!==c)return console.log(`Theme mode fixed to: ${c}. Disabling toggle.`),r&&(r.className="light"===c?"fas fa-sun":"fas fa-moon"),e(t,"light"===c),t.style.opacity="0.4",t.style.pointerEvents="none",t.style.cursor="default",t.setAttribute("aria-disabled","true"),localStorage.removeItem("mw-theme"),void a.classList.remove("mw-theme-light","mw-theme-dark");const s=window.matchMedia("(prefers-color-scheme: light)"),l=window.CSS&&CSS.supports("color","light-dark(#000, #fff)"),i=()=>l&&s.matches,d=localStorage.getItem("mw-theme");let u=d?"light"===d:i();const m=n=>{o.classList.add("mw-theme-switching"),a.classList.toggle("mw-theme-light",n),a.classList.toggle("mw-theme-dark",!n),o.offsetHeight,o.classList.remove("mw-theme-switching"),e(t,n),r&&(r.className=n?"fas fa-sun":"fas fa-moon")};m(u),s.addEventListener("change",()=>{localStorage.getItem("mw-theme")||(u=i(),m(u),n())}),t.addEventListener("click",()=>{u=!u,m(u),localStorage.setItem("mw-theme",u?"light":"dark"),n()})}(),n(),document.querySelectorAll(".mw-accordion-header").forEach(n=>{const a="BUTTON"===n.tagName;a&&n.setAttribute("aria-expanded",String(t(n))),n.addEventListener("click",function(){const n=!t(this);e(this,n),e(this.nextElementSibling,n),a&&this.setAttribute("aria-expanded",String(n))})}),function(){const e=document.querySelector(".mw-menu-btn"),t=document.querySelector(".mw-navbar");if(!e||!t)return;const n="BUTTON"===e.tagName;function a(a){e.classList.toggle("open",a),t.classList.toggle("open",a),n&&e.setAttribute("aria-expanded",String(a))}function o(e){e.preventDefault(),e.stopPropagation(),a(!t.classList.contains("open"))}e.addEventListener("click",o),e.addEventListener("touchstart",o,{passive:!1}),document.addEventListener("click",function(n){t.classList.contains("open")&&(t.contains(n.target)||e.contains(n.target)||a(!1))}),document.addEventListener("keydown",function(n){"Escape"===n.key&&t.classList.contains("open")&&(a(!1),e.focus())})}(),function(){const e=document.querySelectorAll(".mw-progress-fill");if(!e.length)return;const t=CSS.supports("animation-timeline","view()"),n=e=>e.dataset.value?e.dataset.value+"%":e.style.width,a=[];if(e.forEach(e=>{const o=n(e);o&&(e.style.setProperty("--mw-progress-value",o),t?e.classList.add("mw-progress-driven"):a.push(e))}),t)return;const o=new IntersectionObserver((e,t)=>{e.forEach(e=>{e.isIntersecting&&(e.target.style.width=n(e.target),t.unobserve(e.target))})},{root:null,threshold:.2});a.forEach(e=>o.observe(e))}(),function(){if(CSS.supports("animation-timeline","view()"))return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;const e=document.querySelectorAll(".mw-reveal, .mw-reveal-stagger > *");if(!e.length)return;const t=e=>{const t=e.parentElement;if(!t||!t.classList.contains("mw-reveal-stagger"))return 0;const n=getComputedStyle(t).gridTemplateColumns.split(" ").length;return[...t.children].indexOf(e)%n*90},n=new IntersectionObserver((e,n)=>{e.forEach(e=>{e.isIntersecting&&(n.unobserve(e.target),e.target.style.animationDelay=t(e.target)+"ms",e.target.classList.replace("mw-reveal-hidden","mw-reveal-run"))})},{threshold:.15});e.forEach(e=>{e.getBoundingClientRect().top<window.innerHeight||(e.classList.add("mw-reveal-hidden"),n.observe(e))})}(),function(){const e=document.querySelector(".mw-header-reveal");if(!e)return;if(CSS.supports("animation-timeline","scroll()"))return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;const t=document.querySelector(".mw-announcement:not(.mw-announcement-static)"),n=270;let a=null;const o=()=>{const o=window.scrollY<n;o!==a&&(a=o,e.classList.toggle("mw-header-away",o),t&&t.classList.toggle("mw-announcement-away",o))};o(),requestAnimationFrame(()=>{e.classList.add("mw-header-driven"),t&&t.classList.add("mw-announcement-driven")}),window.addEventListener("scroll",o,{passive:!0})}(),function(){if(CSS.supports("animation-timeline","view()"))return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;const e=document.querySelectorAll(".mw-parallax-media");if(!e.length)return;const t=[...e].map(e=>(e.classList.add("mw-parallax-driven"),{el:e,pinned:Boolean(e.closest(".mw-parallax-sticky"))})),n=e=>Math.min(1,Math.max(0,e)),a=(e,t)=>{if(e.pinned)return n(window.scrollY/t);const a=e.el.getBoundingClientRect();return n((t-a.top)/(t+a.height))};let o=!1;const r=()=>{o=!1;const e=window.innerHeight,n=t.map(t=>a(t,e));t.forEach((e,t)=>e.el.style.setProperty("--mw-parallax-progress",n[t]))},c=()=>{o||(o=!0,requestAnimationFrame(r))};r(),window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0})}(),function(){function e(e){const t=e.style.position,n="sticky"===getComputedStyle(e).position;n&&(e.style.position="static");const a=e.getBoundingClientRect().top+window.scrollY;return n&&(e.style.position=t),a}function t(e){const t=window.scrollY,n=e-t;if(!n)return;const a=Math.min(2e3,600+.5*Math.abs(n)),r={};o=r;let c=null;function s(e){if(o!==r)return;null===c&&(c=e);const l=Math.min(1,(e-c)/a),i=l<.5?4*l*l*l:1-Math.pow(-2*l+2,3)/2;window.scrollTo({top:t+n*i,behavior:"instant"}),l<1?requestAnimationFrame(s):o=null}requestAnimationFrame(s)}document.querySelectorAll('a[href^="#"]').forEach(n=>{n.addEventListener("click",function(n){const o=this.getAttribute("href");if("#"===o)return;const r=document.querySelector(o);if(!r)return;const c=document.querySelector(".mw-menu-btn"),s=document.querySelector(".mw-navbar");c&&s&&(c.classList.remove("open"),s.classList.remove("open")),n.preventDefault();const l=Math.max(0,e(r)-a());r.hasAttribute("tabindex")||r.setAttribute("tabindex","-1"),r.focus({preventScroll:!0}),window.matchMedia("(prefers-reduced-motion: reduce)").matches?window.scrollTo({top:l,behavior:"instant"}):t(l),history.replaceState(null,"",o)})})}(),function(){const t=document.querySelectorAll("section[id]"),n=document.querySelectorAll(".mw-navbar-link");if(0===t.length||0===n.length)return;window.addEventListener("scroll",function(e,t){let n;return function(){const a=this,o=arguments;clearTimeout(n),n=setTimeout(()=>e.apply(a,o),t)}}(function(){let o="";const r=a()+2;t.forEach(e=>{const t=e.offsetTop-r,n=e.clientHeight;scrollY>=t&&scrollY<t+n&&(o=e.getAttribute("id"))}),n.forEach(t=>{e(t,!1);const n=t.getAttribute("href");if(n){const a=n.indexOf("#");if(-1!==a){n.substring(a+1)===o&&e(t,!0)}}})},100))}(),document.querySelectorAll(".mw-tabs-nav").forEach(e=>{const t=Array.from(e.querySelectorAll(".mw-tabs-nav-item"));if(!t.length)return;const n=!!e.closest(".mw-tabs-vertical");e.setAttribute("role","tablist"),n&&e.setAttribute("aria-orientation","vertical"),t.forEach((e,a)=>{const o=c(e);e.setAttribute("role","tab"),e.id||(e.id=(o?o.id:"mw-tab-"+a)+"-tab"),o&&(e.setAttribute("aria-controls",o.id),o.setAttribute("role","tabpanel"),o.setAttribute("aria-labelledby",e.id)),s(e),e.addEventListener("click",()=>l(t,e)),e.addEventListener("keydown",e=>{const o=function(e,t,n,a){const o=t?"ArrowUp":"ArrowLeft",r=t?"ArrowDown":"ArrowRight";return e.key===o?(a-1+n)%n:e.key===r?(a+1)%n:"Home"===e.key?0:"End"===e.key?n-1:null}(e,n,t.length,a);null!==o&&(e.preventDefault(),l(t,t[o]),t[o].focus())})}),r(e);const a=new ResizeObserver(()=>r(e));a.observe(e),t.forEach(e=>a.observe(e))}),document.querySelectorAll(".mw-alert-close").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-alert");e&&(e.classList.add("mw-alert-closing"),setTimeout(()=>{e.classList.add("mw-alert-closed")},300))})}),function(){const e=document.querySelector(".mw-localhost-indicator-activated"),t=document.querySelector(".mw-header");if(e){if("localhost"===window.location.hostname||"127.0.0.1"===window.location.hostname||window.location.hostname.includes("192.168.")){const e=document.createElement("div");e.className="mw-localhost-indicator-pulse",t.prepend(e)}}}(),document.querySelectorAll(".mw-slider-container").forEach(e=>{const t=e.querySelector(".mw-slider"),n=e.querySelector(".mw-slider-value");if(!t||!n)return;const a=()=>{const e=Number(t.value),a=Number(t.max)||100,o=Math.round(e/a*100);t.style.setProperty("--value",o+"%"),n.classList.contains("mw-slider-numeric")?n.setAttribute("data-value",e):n.setAttribute("data-value",o)};t.addEventListener("input",a),a()}),document.addEventListener("click",e=>{const t=e.target.closest(".mw-modal-close");if(t){const e=t.closest("dialog.mw-modal");if(e)return void e.close();const n=t.closest(".mw-modal-overlay");return void(n&&n.classList.remove("mw-modal-open"))}const n=e.target.closest("[data-mw-modal]");var a;n?i(document.getElementById(n.dataset.mwModal)):e.target.matches("dialog.mw-modal[open]")&&(!("closedBy"in(a=e.target))||null===a.getAttribute("closedby"))&&e.target.close()}),function(){const e=document.getElementById("login-button");e&&e.addEventListener("click",function(){const e=this.querySelector("i");e.classList.contains("fa-lock")?(e.classList.remove("fa-lock"),e.classList.add("fa-lock-open")):(e.classList.remove("fa-lock-open"),e.classList.add("fa-lock"))})}(),document.querySelectorAll(".mw-image-slider").forEach(t=>{const n=t.querySelectorAll(".mw-image-slider-overlay-image"),a=t.querySelectorAll("button[data-index]");if(!n.length||!a.length)return;let o=0;function r(){n.forEach(t=>{e(t,parseInt(t.dataset.index)===o)}),a.forEach(t=>{e(t,parseInt(t.dataset.index)===o)})}function c(e){o=e,r()}a.forEach(e=>{e.addEventListener("click",()=>{c(parseInt(e.dataset.index))})}),r()}),document.querySelectorAll(".mw-item-list-checkbox, .mw-item-list-checkbox-scroll").forEach(e=>{e.querySelectorAll("li").forEach(e=>{const t=e.querySelector('input[type="checkbox"]'),n=e.querySelector(".mw-checkbox");t&&n&&(t.checked&&e.classList.add("mw-selected"),e.addEventListener("click",function(e){e.target!==t&&e.target.closest(".mw-checkbox")!==n&&toggleCheckbox(this)}),t.addEventListener("change",function(){const e=this.closest("li");this.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:this.checked,item:e}}))}),n.addEventListener("click",function(e){setTimeout(()=>{const e=this.closest("li");this.querySelector('input[type="checkbox"]').checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected")},0)}))})}),document.addEventListener("checkboxToggle",function(e){console.log("Checkbox toggled:",e.detail.checked,e.detail.item)}),document.querySelectorAll(".mw-kanban").forEach(d),document.querySelectorAll("[data-calendar]").forEach(u),function(){function e(e){document.querySelectorAll(".mw-dropdown[open]").forEach(t=>{t!==e&&t.removeAttribute("open")})}document.addEventListener("click",function(t){const n=t.target.closest(".mw-dropdown");e(n),n&&t.target.closest(".mw-dropdown-item")&&n.removeAttribute("open")}),document.addEventListener("keydown",function(e){if("Escape"!==e.key)return;const t=document.querySelector(".mw-dropdown[open]");if(!t)return;t.removeAttribute("open");const n=t.querySelector("summary");n&&n.focus()})}(),document.querySelectorAll(".mw-lang-switch").forEach(t=>{const n=t.querySelectorAll("[data-mw-lang]"),a=t.querySelector("summary"),o=t.querySelector("summary .mw-flag"),r=t.querySelector("summary .mw-lang-switch-code");n.forEach(c=>{c.addEventListener("click",function(){const s=c.dataset.mwLang,l=c.querySelector(".mw-lang-switch-name"),i=l?l.textContent.trim():s;n.forEach(t=>{e(t,t===c),t===c?t.setAttribute("aria-current","true"):t.removeAttribute("aria-current")}),r&&(r.textContent=s);const d=c.querySelector(".mw-flag");o&&d&&(o.className=d.className),a&&a.setAttribute("aria-label","Language: "+i),t.dispatchEvent(new CustomEvent("mw-language-change",{bubbles:!0,detail:{lang:s,name:i}}))})})})}),window.toggleCheckbox=function(e){const t=e.querySelector('input[type="checkbox"]');if(!t)return;const n=t.checked;t.checked=!n,t.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:t.checked,item:e}}))};let o=null;function r(e){const t=e.querySelector(".mw-tabs-nav-item.mw-active, .mw-tabs-nav-item.active");t?(e.style.setProperty("--mw-tabs-marker-x",t.offsetLeft+"px"),e.style.setProperty("--mw-tabs-marker-w",t.offsetWidth),e.style.setProperty("--mw-tabs-marker-y",t.offsetTop+"px"),e.style.setProperty("--mw-tabs-marker-h",t.offsetHeight),e.setAttribute("data-mw-tabs-marker","")):e.removeAttribute("data-mw-tabs-marker")}function c(e){const t=e.getAttribute("data-tab");return t?document.getElementById(t):null}function s(e){const n=t(e);e.setAttribute("aria-selected",String(n)),e.tabIndex=n?0:-1}function l(t,n){t.forEach(t=>{const a=t===n;e(t,a),e(c(t),a),s(t)});const a=n.closest(".mw-tabs-nav");a&&r(a)}function i(e){e&&("DIALOG"===e.tagName?e.showModal():e.classList.add("mw-modal-open"))}function d(e){const t=Array.from(e.querySelectorAll(".mw-kanban-column")),n=e.querySelector(".mw-kanban-composer"),a=e.querySelector(".mw-kanban-card-template"),o=e.dataset.kanbanPrefix||"MW",r=n.querySelector(".mw-kanban-composer-title"),c=n.querySelector(".mw-kanban-composer-text"),s=n.querySelector(".mw-kanban-composer-priority"),l=n.querySelector(".mw-kanban-composer-assignee");let i=null;function d(){t.forEach(function(e,n){const a=e.querySelectorAll(".mw-kanban-card"),o=e.querySelector(".mw-kanban-count");o&&(o.textContent=a.length),e.querySelectorAll("[data-kanban-move]").forEach(function(e){const a=n+parseInt(e.dataset.kanbanMove,10);e.disabled=a<0||a>=t.length})})}function u(e,t){const n=e.querySelector(t);return n?n.textContent.replace(/\s+/g," ").trim():""}function m(e,t){w(),i=t||null,r.value=t?u(t,".mw-kanban-card-title"):"",c.value=t?u(t,".mw-kanban-card-text"):"",s.value=t?function(e){const t=e.querySelector(".mw-card-ribbon");if(!t)return"";const n=Array.from(t.classList).find(function(e){return 0===e.indexOf("mw-card-addon-")});return n?n.replace("mw-card-addon-",""):""}(t):"",l.value=t?function(e){const t=e.querySelector(".mw-avatar-initials");return t?t.textContent.trim():""}(t):"",t?(t.classList.add("mw-kanban-editing"),t.before(n)):e.querySelector(".mw-kanban-column-body").prepend(n),n.classList.add("mw-active"),r.focus()}function w(){n.classList.remove("mw-active"),i&&i.classList.remove("mw-kanban-editing"),i=null}function f(){const t=r.value.trim();if(!t)return void r.focus();const u=i||a.content.firstElementChild.cloneNode(!0);if(u.querySelector(".mw-kanban-card-title").textContent=t,u.querySelector(".mw-kanban-card-text").textContent=c.value.trim(),function(e,t){const n=e.querySelector(".mw-card-ribbon");if(n&&n.remove(),!t||!t.value)return;const a=document.createElement("div");a.className="mw-card-ribbon mw-card-addon-"+t.value,a.textContent=t.textContent,e.prepend(a)}(u,s.selectedOptions[0]),function(e,t){const n=e.querySelector(".mw-kanban-card-actions"),a=n.querySelector(".mw-avatar");if(!t)return void(a&&a.remove());const o=a||document.createElement("div");o.className="mw-avatar mw-avatar-xs mw-avatar-initials",o.textContent=t,a||n.prepend(o)}(u,l.value),!i){const t=function(){const t=Array.from(e.querySelectorAll(".mw-kanban-card-id")).map(function(e){return parseInt(e.textContent.split("-").pop(),10)||0});return o+"-"+(Math.max(0,...t)+1)}();u.classList.add("mw-kanban-card-in"),u.querySelector(".mw-kanban-card-id").textContent=t,u.querySelectorAll("[aria-label]").forEach(function(e){e.setAttribute("aria-label",e.getAttribute("aria-label").replace(/[A-Z]+-\d+/,t))}),n.before(u)}w(),d()}const h=["mw-kanban-card-moved-forward","mw-kanban-card-moved-back"];e.addEventListener("click",function(e){const n=e.target.closest("[data-kanban-move]");if(n){if(function(e,n){const a=e.closest(".mw-kanban-column"),o=t[t.indexOf(a)+n];o&&(o.querySelector(".mw-kanban-column-body").appendChild(e),e.classList.remove.apply(e.classList,h),e.offsetWidth,e.classList.add(n>0?h[0]:h[1]),d())}(n.closest(".mw-kanban-card"),parseInt(n.dataset.kanbanMove,10)),n.disabled){const e=n.parentElement.querySelector("[data-kanban-move]:not(:disabled)");e&&e.focus()}return}const a=e.target.closest(".mw-kanban-edit");if(a){const e=a.closest(".mw-kanban-card");return void m(e.closest(".mw-kanban-column"),e)}const o=e.target.closest(".mw-kanban-delete");if(o){const e=o.closest(".mw-kanban-card");return e===i&&w(),e.remove(),void d()}const r=e.target.closest(".mw-kanban-add");r?m(r.closest(".mw-kanban-column"),null):e.target.closest(".mw-kanban-composer-cancel")&&w()}),n.addEventListener("submit",function(e){e.preventDefault(),f()}),n.addEventListener("keydown",function(e){"Escape"===e.key&&w()}),d()}function u(e){const t=e.querySelector(".mw-calendar-grid"),n=e.querySelector(".mw-calendar-title");if(!t)return;const a="week"===e.dataset.calendar,o=function(e){if(!e)return{};try{return JSON.parse(e)}catch(e){return{}}}(e.dataset.calendarMarkers),r=e.dataset.calendarLocale||document.documentElement.lang||void 0,c=new Date;c.setHours(0,0,0,0);let s=e.dataset.calendarDate?new Date(e.dataset.calendarDate+"T00:00:00"):new Date(c),l=e.dataset.calendarSelected||"";function i(e){const t=new Date(e);return t.setDate(t.getDate()-(t.getDay()+6)%7),t}function d(e){return e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0")}function u(){if(!a)return s.toLocaleDateString(r,{month:"long",year:"numeric"});const e=i(s),t=new Date(e);t.setDate(t.getDate()+6);const n=e.toLocaleDateString(r,{day:"numeric",month:e.getMonth()===t.getMonth()?void 0:"short"}),o=t.toLocaleDateString(r,{day:"numeric",month:"short",year:"numeric"});return"Week "+function(e){const t=i(e);t.setDate(t.getDate()+3);const n=i(new Date(t.getFullYear(),0,4));return n.setDate(n.getDate()+3),1+Math.round((t-n)/6048e5)}(e)+" · "+n+" - "+o}function m(e){const t=e.getDay();return 0===t||6===t}function w(e,t){const n=d(e),s=document.createElement("button");s.type="button",s.className="mw-calendar-day",s.tabIndex=-1,s.dataset.calendarDay=n,s.setAttribute("aria-pressed",n===l?"true":"false"),s.setAttribute("aria-label",e.toLocaleDateString(r,{weekday:"long",day:"numeric",month:"long",year:"numeric"})),m(e)&&s.classList.add("mw-calendar-weekend"),a||e.getMonth()===t||s.classList.add("mw-calendar-adjacent"),n===d(c)&&(s.classList.add("mw-calendar-today"),s.setAttribute("aria-current","date")),n===l&&s.classList.add("mw-selected");const i=document.createElement("span");i.className="mw-calendar-date",i.textContent=e.getDate(),s.appendChild(i);const u=[].concat(o[n]||[]);if(u.length){const e=document.createElement("span");e.className="mw-calendar-dots",u.forEach(function(t){const n=document.createElement("span");n.className=t?"mw-calendar-dot mw-calendar-dot-"+t:"mw-calendar-dot",e.appendChild(n)}),s.appendChild(e)}return s}function f(){const e=s.getMonth(),o=a?7:42,c=i(a?s:new Date(s.getFullYear(),e,1));t.innerHTML="",function(){const e=i(new Date);for(let n=0;n<7;n++){const n=document.createElement("div");n.className="mw-calendar-weekday",m(e)&&n.classList.add("mw-calendar-weekend"),n.setAttribute("aria-hidden","true"),n.textContent=e.toLocaleDateString(r,{weekday:"short"}),t.appendChild(n),e.setDate(e.getDate()+1)}}();for(let n=0;n<o;n++)t.appendChild(w(c,e)),c.setDate(c.getDate()+1);n&&(n.textContent=u()),function(){const e=t.querySelector(".mw-selected")||t.querySelector(".mw-calendar-today")||t.querySelector(".mw-calendar-day:not(.mw-calendar-adjacent)")||t.querySelector(".mw-calendar-day");e&&(e.tabIndex=0)}()}e.addEventListener("click",function(n){const o=n.target.closest("[data-calendar-nav]");if(o)return r=parseInt(o.dataset.calendarNav,10),a?s.setDate(s.getDate()+7*r):s=new Date(s.getFullYear(),s.getMonth()+r,1),void f();var r;const c=n.target.closest(".mw-calendar-day");var i;c&&!c.disabled&&(i=c.dataset.calendarDay,l=i===l?"":i,t.querySelectorAll(".mw-calendar-day").forEach(function(e){const t=e.dataset.calendarDay===l;e.classList.toggle("mw-selected",t),e.setAttribute("aria-pressed",t?"true":"false")}),e.dispatchEvent(new CustomEvent("mw-calendar-select",{bubbles:!0,detail:{date:l}})))}),t.addEventListener("keydown",function(e){const n={ArrowLeft:-1,ArrowRight:1,ArrowUp:-7,ArrowDown:7}[e.key];if(!n)return;const a=e.target.closest(".mw-calendar-day");if(!a)return;e.preventDefault();const o=new Date(a.dataset.calendarDay+"T00:00:00");o.setDate(o.getDate()+n),function(e){const n=d(e);let a=t.querySelector('[data-calendar-day="'+n+'"]');a||(s=new Date(e),f(),a=t.querySelector('[data-calendar-day="'+n+'"]')),a&&(t.querySelectorAll(".mw-calendar-day").forEach(function(e){e.tabIndex=-1}),a.tabIndex=0,a.focus())}(o)}),f()}["wheel","touchstart"].forEach(e=>{window.addEventListener(e,()=>o=null,{passive:!0})}),window.mwOpenModal=e=>i(document.getElementById(e)),window.mwCloseModal=e=>{var t;(t=document.getElementById(e))&&("DIALOG"===t.tagName?t.close():t.classList.remove("mw-modal-open"))},window.mwRefreshColorSwatches=n}();
1
+ !function(){"use strict";function e(e,t){e&&(e.classList.toggle("mw-active",t),t||e.classList.remove("active"))}function t(e){return!!e&&(e.classList.contains("mw-active")||e.classList.contains("active"))}function n(){const e=Array.from(document.querySelectorAll(".color-swatch")),t=e.map(e=>function(e){if(!e||"transparent"===e)return"#000000";const t=e=>Math.max(0,Math.min(255,Math.round(e))).toString(16).padStart(2,"0"),n=e.match(/color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\)/);if(n){const e="#"+t(255*n[1])+t(255*n[2])+t(255*n[3]),a=void 0===n[4]?1:parseFloat(n[4]);return(a<1?`${e} / ${a}`:e).toUpperCase()}const a=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(!a)return e;const o="#"+t(a[1])+t(a[2])+t(a[3]),r=void 0===a[4]?1:parseFloat(a[4]);return(r<1?`${o} / ${r}`:o).toUpperCase()}(window.getComputedStyle(e).backgroundColor));e.forEach((e,n)=>{const a=e.parentElement.querySelector(".mw-text-muted");a&&(a.textContent=t[n])})}function a(){const e=parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop);return Number.isNaN(e)?0:e}document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".mw-gallery").forEach(e=>{const t=e.closest(".mw-gallery-container")||e.parentElement;t&&function(e,t){const n=t.querySelector(".mw-gallery-track");if(!n||!n.children.length)return;const a=n.children,o=e.querySelector(".mw-gallery-dots"),r=e.querySelector(".mw-gallery-desc"),c=t.querySelector(".mw-gallery-navi-prev"),s=t.querySelector(".mw-gallery-navi-next");let l=0;function i(e){const t=a.length;l=(e+t)%t,n.style.transform=`translateX(-${100*l}%)`,o&&o.querySelectorAll(".mw-gallery-dot").forEach((e,t)=>{e.classList.toggle("mw-active",t===l)}),r&&(r.textContent=a[l].dataset.desc||"")}if(o)for(let e=0;e<a.length;e++){const t=document.createElement("button");t.type="button",t.className="mw-gallery-dot"+(0===e?" mw-active":""),t.setAttribute("aria-label","Go to slide "+(e+1)),t.addEventListener("click",()=>i(e)),o.appendChild(t)}c&&c.addEventListener("click",()=>i(l-1)),s&&s.addEventListener("click",()=>i(l+1));let d=0,u=0,m=!1;n.addEventListener("touchstart",e=>{m=1===e.touches.length,m&&(d=e.touches[0].clientX,u=e.touches[0].clientY)},{passive:!0}),n.addEventListener("touchcancel",()=>m=!1),n.addEventListener("touchend",e=>{if(!m)return;m=!1;const t=e.changedTouches[0].clientX-d,n=e.changedTouches[0].clientY-u;Math.abs(t)<50||Math.abs(t)<=Math.abs(n)||i(l+(t<0?1:-1))},{passive:!0}),i(0)}(t,e)}),function(){const t=document.querySelector(".mw-theme-toggle");if(!t)return;const a=document.body,o=document.documentElement,r=t.querySelector(".mw-theme-toggle-slider i"),c=getComputedStyle(document.documentElement).getPropertyValue("--mw-internal-theme-mode").trim()||"switchable";if("switchable"!==c)return console.log(`Theme mode fixed to: ${c}. Disabling toggle.`),r&&(r.className="light"===c?"fas fa-sun":"fas fa-moon"),e(t,"light"===c),t.style.opacity="0.4",t.style.pointerEvents="none",t.style.cursor="default",t.setAttribute("aria-disabled","true"),localStorage.removeItem("mw-theme"),void a.classList.remove("mw-theme-light","mw-theme-dark");const s=window.matchMedia("(prefers-color-scheme: light)"),l=window.CSS&&CSS.supports("color","light-dark(#000, #fff)"),i=()=>l&&s.matches,d=localStorage.getItem("mw-theme");let u=d?"light"===d:i();const m=n=>{o.classList.add("mw-theme-switching"),a.classList.toggle("mw-theme-light",n),a.classList.toggle("mw-theme-dark",!n),o.offsetHeight,o.classList.remove("mw-theme-switching"),e(t,n),r&&(r.className=n?"fas fa-sun":"fas fa-moon")};m(u),s.addEventListener("change",()=>{localStorage.getItem("mw-theme")||(u=i(),m(u),n())}),t.addEventListener("click",()=>{u=!u,m(u),localStorage.setItem("mw-theme",u?"light":"dark"),n()})}(),n(),document.querySelectorAll(".mw-accordion-header").forEach(n=>{const a="BUTTON"===n.tagName;a&&n.setAttribute("aria-expanded",String(t(n))),n.addEventListener("click",function(){const n=!t(this);e(this,n),e(this.nextElementSibling,n),a&&this.setAttribute("aria-expanded",String(n))})}),function(){const e=document.querySelector(".mw-menu-btn"),t=document.querySelector(".mw-navbar");if(!e||!t)return;const n="BUTTON"===e.tagName;function a(a){e.classList.toggle("open",a),t.classList.toggle("open",a),n&&e.setAttribute("aria-expanded",String(a))}function o(e){e.preventDefault(),e.stopPropagation(),a(!t.classList.contains("open"))}e.addEventListener("click",o),e.addEventListener("touchstart",o,{passive:!1}),document.addEventListener("click",function(n){t.classList.contains("open")&&(t.contains(n.target)||e.contains(n.target)||a(!1))}),document.addEventListener("keydown",function(n){"Escape"===n.key&&t.classList.contains("open")&&(a(!1),e.focus())})}(),function(){const e=document.querySelectorAll(".mw-progress-fill");if(!e.length)return;const t=CSS.supports("animation-timeline","view()"),n=e=>e.dataset.value?e.dataset.value+"%":e.style.width,a=[];if(e.forEach(e=>{const o=n(e);o&&(e.style.setProperty("--mw-progress-value",o),t?e.classList.add("mw-progress-driven"):a.push(e))}),t)return;const o=new IntersectionObserver((e,t)=>{e.forEach(e=>{e.isIntersecting&&(e.target.style.width=n(e.target),t.unobserve(e.target))})},{root:null,threshold:.2});a.forEach(e=>o.observe(e))}(),function(){if(CSS.supports("animation-timeline","view()"))return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;const e=document.querySelectorAll(".mw-reveal, .mw-reveal-stagger > *");if(!e.length)return;const t=e=>{const t=e.parentElement;if(!t||!t.classList.contains("mw-reveal-stagger"))return 0;const n=getComputedStyle(t).gridTemplateColumns.split(" ").length;return[...t.children].indexOf(e)%n*90},n=new IntersectionObserver((e,n)=>{e.forEach(e=>{e.isIntersecting&&(n.unobserve(e.target),e.target.style.animationDelay=t(e.target)+"ms",e.target.classList.replace("mw-reveal-hidden","mw-reveal-run"))})},{threshold:.15});e.forEach(e=>{e.getBoundingClientRect().top<window.innerHeight||(e.classList.add("mw-reveal-hidden"),n.observe(e))})}(),function(){const e=document.querySelector(".mw-header-reveal");if(!e)return;if(CSS.supports("animation-timeline","scroll()"))return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;const t=document.querySelector(".mw-announcement:not(.mw-announcement-static)"),n=270;let a=null;const o=()=>{const o=window.scrollY<n;o!==a&&(a=o,e.classList.toggle("mw-header-away",o),t&&t.classList.toggle("mw-announcement-away",o))};o(),requestAnimationFrame(()=>{e.classList.add("mw-header-driven"),t&&t.classList.add("mw-announcement-driven")}),window.addEventListener("scroll",o,{passive:!0})}(),function(){if(CSS.supports("animation-timeline","view()"))return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches)return;const e=document.querySelectorAll(".mw-parallax-media");if(!e.length)return;const t=[...e].map(e=>(e.classList.add("mw-parallax-driven"),{el:e,pinned:Boolean(e.closest(".mw-parallax-sticky"))})),n=e=>Math.min(1,Math.max(0,e)),a=(e,t)=>{if(e.pinned)return n(window.scrollY/t);const a=e.el.getBoundingClientRect();return n((t-a.top)/(t+a.height))};let o=!1;const r=()=>{o=!1;const e=document.documentElement.clientHeight,n=t.map(t=>a(t,e));t.forEach((e,t)=>e.el.style.setProperty("--mw-parallax-progress",n[t]))},c=()=>{o||(o=!0,requestAnimationFrame(r))};r(),window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0})}(),function(){function e(e){const t=e.style.position,n="sticky"===getComputedStyle(e).position;n&&(e.style.position="static");const a=e.getBoundingClientRect().top+window.scrollY;return n&&(e.style.position=t),a}function t(e){const t=window.scrollY,n=e-t;if(!n)return;const a=Math.min(2e3,600+.5*Math.abs(n)),r={};o=r;let c=null;function s(e){if(o!==r)return;null===c&&(c=e);const l=Math.min(1,(e-c)/a),i=l<.5?4*l*l*l:1-Math.pow(-2*l+2,3)/2;window.scrollTo({top:t+n*i,behavior:"instant"}),l<1?requestAnimationFrame(s):o=null}requestAnimationFrame(s)}document.querySelectorAll('a[href^="#"]').forEach(n=>{n.addEventListener("click",function(n){const o=this.getAttribute("href");if("#"===o)return;const r=document.querySelector(o);if(!r)return;const c=document.querySelector(".mw-menu-btn"),s=document.querySelector(".mw-navbar");c&&s&&(c.classList.remove("open"),s.classList.remove("open")),n.preventDefault();const l=Math.max(0,e(r)-a());r.hasAttribute("tabindex")||r.setAttribute("tabindex","-1"),r.focus({preventScroll:!0}),window.matchMedia("(prefers-reduced-motion: reduce)").matches?window.scrollTo({top:l,behavior:"instant"}):t(l),history.replaceState(null,"",o)})})}(),function(){const t=document.querySelectorAll("section[id]"),n=document.querySelectorAll(".mw-navbar-link");if(0===t.length||0===n.length)return;window.addEventListener("scroll",function(e,t){let n;return function(){const a=this,o=arguments;clearTimeout(n),n=setTimeout(()=>e.apply(a,o),t)}}(function(){let o="";const r=a()+2;t.forEach(e=>{const t=e.offsetTop-r,n=e.clientHeight;scrollY>=t&&scrollY<t+n&&(o=e.getAttribute("id"))}),n.forEach(t=>{e(t,!1);const n=t.getAttribute("href");if(n){const a=n.indexOf("#");if(-1!==a){n.substring(a+1)===o&&e(t,!0)}}})},100),{passive:!0})}(),document.querySelectorAll(".mw-tabs-nav").forEach(e=>{const t=Array.from(e.querySelectorAll(".mw-tabs-nav-item"));if(!t.length)return;const n=!!e.closest(".mw-tabs-vertical");e.setAttribute("role","tablist"),n&&e.setAttribute("aria-orientation","vertical"),t.forEach((e,a)=>{const o=c(e);e.setAttribute("role","tab"),e.id||(e.id=(o?o.id:"mw-tab-"+a)+"-tab"),o&&(e.setAttribute("aria-controls",o.id),o.setAttribute("role","tabpanel"),o.setAttribute("aria-labelledby",e.id)),s(e),e.addEventListener("click",()=>l(t,e)),e.addEventListener("keydown",e=>{const o=function(e,t,n,a){const o=t?"ArrowUp":"ArrowLeft",r=t?"ArrowDown":"ArrowRight";return e.key===o?(a-1+n)%n:e.key===r?(a+1)%n:"Home"===e.key?0:"End"===e.key?n-1:null}(e,n,t.length,a);null!==o&&(e.preventDefault(),l(t,t[o]),t[o].focus())})}),r(e);const a=new ResizeObserver(()=>r(e));a.observe(e),t.forEach(e=>a.observe(e))}),document.querySelectorAll(".mw-alert-close").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-alert");e&&(e.classList.add("mw-alert-closing"),setTimeout(()=>{e.classList.add("mw-alert-closed")},300))})}),function(){const e=document.querySelector(".mw-localhost-indicator-activated"),t=document.querySelector(".mw-header");if(e&&i()){const e=document.createElement("div");e.className="mw-localhost-indicator-pulse",t.prepend(e)}}(),function(){if(!i())return;const e=document.querySelector('meta[name="viewport"]'),t=e&&e.getAttribute("content")||"";if(/viewport-fit\s*=\s*cover/.test(t))return;console.warn('MaverickWave: <meta name="viewport"> is missing viewport-fit=cover. env(safe-area-inset-*) resolves to 0 without it, so notched phones lose the gutter, the nav panel and the modal padding that expect it.')}(),document.querySelectorAll(".mw-slider-container").forEach(e=>{const t=e.querySelector(".mw-slider"),n=e.querySelector(".mw-slider-value");if(!t||!n)return;const a=()=>{const e=Number(t.value),a=Number(t.max)||100,o=Math.round(e/a*100);t.style.setProperty("--value",o+"%"),n.classList.contains("mw-slider-numeric")?n.setAttribute("data-value",e):n.setAttribute("data-value",o)};t.addEventListener("input",a),a()}),document.addEventListener("click",e=>{const t=e.target.closest(".mw-modal-close");if(t){const e=t.closest("dialog.mw-modal");if(e)return void e.close();const n=t.closest(".mw-modal-overlay");return void(n&&n.classList.remove("mw-modal-open"))}const n=e.target.closest("[data-mw-modal]");var a;n?d(document.getElementById(n.dataset.mwModal)):e.target.matches("dialog.mw-modal[open]")&&(!("closedBy"in(a=e.target))||null===a.getAttribute("closedby"))&&e.target.close()}),function(){const e=document.getElementById("login-button");e&&e.addEventListener("click",function(){const e=this.querySelector("i");e.classList.contains("fa-lock")?(e.classList.remove("fa-lock"),e.classList.add("fa-lock-open")):(e.classList.remove("fa-lock-open"),e.classList.add("fa-lock"))})}(),document.querySelectorAll(".mw-image-slider").forEach(t=>{const n=t.querySelectorAll(".mw-image-slider-overlay-image"),a=t.querySelectorAll("button[data-index]");if(!n.length||!a.length)return;let o=0;function r(){n.forEach(t=>{e(t,parseInt(t.dataset.index)===o)}),a.forEach(t=>{e(t,parseInt(t.dataset.index)===o)})}function c(e){o=e,r()}a.forEach(e=>{e.addEventListener("click",()=>{c(parseInt(e.dataset.index))})}),r()}),document.querySelectorAll(".mw-item-list-checkbox, .mw-item-list-checkbox-scroll").forEach(e=>{e.querySelectorAll("li").forEach(e=>{const t=e.querySelector('input[type="checkbox"]'),n=e.querySelector(".mw-checkbox");t&&n&&(t.checked&&e.classList.add("mw-selected"),e.addEventListener("click",function(e){e.target!==t&&e.target.closest(".mw-checkbox")!==n&&toggleCheckbox(this)}),t.addEventListener("change",function(){const e=this.closest("li");this.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:this.checked,item:e}}))}),n.addEventListener("click",function(e){setTimeout(()=>{const e=this.closest("li");this.querySelector('input[type="checkbox"]').checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected")},0)}))})}),document.addEventListener("checkboxToggle",function(e){console.log("Checkbox toggled:",e.detail.checked,e.detail.item)}),document.querySelectorAll(".mw-kanban").forEach(u),document.querySelectorAll("[data-calendar]").forEach(m),function(){function e(e){document.querySelectorAll(".mw-dropdown[open]").forEach(t=>{t!==e&&t.removeAttribute("open")})}document.addEventListener("click",function(t){const n=t.target.closest(".mw-dropdown");e(n),n&&t.target.closest(".mw-dropdown-item")&&n.removeAttribute("open")}),document.addEventListener("keydown",function(e){if("Escape"!==e.key)return;const t=document.querySelector(".mw-dropdown[open]");if(!t)return;t.removeAttribute("open");const n=t.querySelector("summary");n&&n.focus()})}(),document.querySelectorAll(".mw-lang-switch").forEach(t=>{const n=t.querySelectorAll("[data-mw-lang]"),a=t.querySelector("summary"),o=t.querySelector("summary .mw-flag"),r=t.querySelector("summary .mw-lang-switch-code");n.forEach(c=>{c.addEventListener("click",function(){const s=c.dataset.mwLang,l=c.querySelector(".mw-lang-switch-name"),i=l?l.textContent.trim():s;n.forEach(t=>{e(t,t===c),t===c?t.setAttribute("aria-current","true"):t.removeAttribute("aria-current")}),r&&(r.textContent=s);const d=c.querySelector(".mw-flag");o&&d&&(o.className=d.className),a&&a.setAttribute("aria-label","Language: "+i),t.dispatchEvent(new CustomEvent("mw-language-change",{bubbles:!0,detail:{lang:s,name:i}}))})})})}),window.toggleCheckbox=function(e){const t=e.querySelector('input[type="checkbox"]');if(!t)return;const n=t.checked;t.checked=!n,t.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:t.checked,item:e}}))};let o=null;function r(e){const t=e.querySelector(".mw-tabs-nav-item.mw-active, .mw-tabs-nav-item.active");t?(e.style.setProperty("--mw-tabs-marker-x",t.offsetLeft+"px"),e.style.setProperty("--mw-tabs-marker-w",t.offsetWidth),e.style.setProperty("--mw-tabs-marker-y",t.offsetTop+"px"),e.style.setProperty("--mw-tabs-marker-h",t.offsetHeight),e.setAttribute("data-mw-tabs-marker","")):e.removeAttribute("data-mw-tabs-marker")}function c(e){const t=e.getAttribute("data-tab");return t?document.getElementById(t):null}function s(e){const n=t(e);e.setAttribute("aria-selected",String(n)),e.tabIndex=n?0:-1}function l(t,n){t.forEach(t=>{const a=t===n;e(t,a),e(c(t),a),s(t)});const a=n.closest(".mw-tabs-nav");a&&r(a)}function i(){const e=window.location.hostname;return"localhost"===e||"127.0.0.1"===e||e.includes("192.168.")}function d(e){e&&("DIALOG"===e.tagName?e.showModal():e.classList.add("mw-modal-open"))}function u(e){const t=Array.from(e.querySelectorAll(".mw-kanban-column")),n=e.querySelector(".mw-kanban-composer"),a=e.querySelector(".mw-kanban-card-template"),o=e.dataset.kanbanPrefix||"MW",r=n.querySelector(".mw-kanban-composer-title"),c=n.querySelector(".mw-kanban-composer-text"),s=n.querySelector(".mw-kanban-composer-priority"),l=n.querySelector(".mw-kanban-composer-assignee");let i=null;function d(){t.forEach(function(e,n){const a=e.querySelectorAll(".mw-kanban-card"),o=e.querySelector(".mw-kanban-count");o&&(o.textContent=a.length),e.querySelectorAll("[data-kanban-move]").forEach(function(e){const a=n+parseInt(e.dataset.kanbanMove,10);e.disabled=a<0||a>=t.length})})}function u(e,t){const n=e.querySelector(t);return n?n.textContent.replace(/\s+/g," ").trim():""}function m(e,t){w(),i=t||null,r.value=t?u(t,".mw-kanban-card-title"):"",c.value=t?u(t,".mw-kanban-card-text"):"",s.value=t?function(e){const t=e.querySelector(".mw-card-ribbon");if(!t)return"";const n=Array.from(t.classList).find(function(e){return 0===e.indexOf("mw-card-addon-")});return n?n.replace("mw-card-addon-",""):""}(t):"",l.value=t?function(e){const t=e.querySelector(".mw-avatar-initials");return t?t.textContent.trim():""}(t):"",t?(t.classList.add("mw-kanban-editing"),t.before(n)):e.querySelector(".mw-kanban-column-body").prepend(n),n.classList.add("mw-active"),r.focus()}function w(){n.classList.remove("mw-active"),i&&i.classList.remove("mw-kanban-editing"),i=null}function f(){const t=r.value.trim();if(!t)return void r.focus();const u=i||a.content.firstElementChild.cloneNode(!0);if(u.querySelector(".mw-kanban-card-title").textContent=t,u.querySelector(".mw-kanban-card-text").textContent=c.value.trim(),function(e,t){const n=e.querySelector(".mw-card-ribbon");if(n&&n.remove(),!t||!t.value)return;const a=document.createElement("div");a.className="mw-card-ribbon mw-card-addon-"+t.value,a.textContent=t.textContent,e.prepend(a)}(u,s.selectedOptions[0]),function(e,t){const n=e.querySelector(".mw-kanban-card-actions"),a=n.querySelector(".mw-avatar");if(!t)return void(a&&a.remove());const o=a||document.createElement("div");o.className="mw-avatar mw-avatar-xs mw-avatar-initials",o.textContent=t,a||n.prepend(o)}(u,l.value),!i){const t=function(){const t=Array.from(e.querySelectorAll(".mw-kanban-card-id")).map(function(e){return parseInt(e.textContent.split("-").pop(),10)||0});return o+"-"+(Math.max(0,...t)+1)}();u.classList.add("mw-kanban-card-in"),u.querySelector(".mw-kanban-card-id").textContent=t,u.querySelectorAll("[aria-label]").forEach(function(e){e.setAttribute("aria-label",e.getAttribute("aria-label").replace(/[A-Z]+-\d+/,t))}),n.before(u)}w(),d()}const h=["mw-kanban-card-moved-forward","mw-kanban-card-moved-back"];e.addEventListener("click",function(e){const n=e.target.closest("[data-kanban-move]");if(n){if(function(e,n){const a=e.closest(".mw-kanban-column"),o=t[t.indexOf(a)+n];o&&(o.querySelector(".mw-kanban-column-body").appendChild(e),e.classList.remove.apply(e.classList,h),e.offsetWidth,e.classList.add(n>0?h[0]:h[1]),d())}(n.closest(".mw-kanban-card"),parseInt(n.dataset.kanbanMove,10)),n.disabled){const e=n.parentElement.querySelector("[data-kanban-move]:not(:disabled)");e&&e.focus()}return}const a=e.target.closest(".mw-kanban-edit");if(a){const e=a.closest(".mw-kanban-card");return void m(e.closest(".mw-kanban-column"),e)}const o=e.target.closest(".mw-kanban-delete");if(o){const e=o.closest(".mw-kanban-card");return e===i&&w(),e.remove(),void d()}const r=e.target.closest(".mw-kanban-add");r?m(r.closest(".mw-kanban-column"),null):e.target.closest(".mw-kanban-composer-cancel")&&w()}),n.addEventListener("submit",function(e){e.preventDefault(),f()}),n.addEventListener("keydown",function(e){"Escape"===e.key&&w()}),d()}function m(e){const t=e.querySelector(".mw-calendar-grid"),n=e.querySelector(".mw-calendar-title");if(!t)return;const a="week"===e.dataset.calendar,o=function(e){if(!e)return{};try{return JSON.parse(e)}catch(e){return{}}}(e.dataset.calendarMarkers),r=e.dataset.calendarLocale||document.documentElement.lang||void 0,c=new Date;c.setHours(0,0,0,0);let s=e.dataset.calendarDate?new Date(e.dataset.calendarDate+"T00:00:00"):new Date(c),l=e.dataset.calendarSelected||"";function i(e){const t=new Date(e);return t.setDate(t.getDate()-(t.getDay()+6)%7),t}function d(e){return e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0")}function u(){if(!a)return s.toLocaleDateString(r,{month:"long",year:"numeric"});const e=i(s),t=new Date(e);t.setDate(t.getDate()+6);const n=e.toLocaleDateString(r,{day:"numeric",month:e.getMonth()===t.getMonth()?void 0:"short"}),o=t.toLocaleDateString(r,{day:"numeric",month:"short",year:"numeric"});return"Week "+function(e){const t=i(e);t.setDate(t.getDate()+3);const n=i(new Date(t.getFullYear(),0,4));return n.setDate(n.getDate()+3),1+Math.round((t-n)/6048e5)}(e)+" · "+n+" - "+o}function m(e){const t=e.getDay();return 0===t||6===t}function w(e,t){const n=d(e),s=document.createElement("button");s.type="button",s.className="mw-calendar-day",s.tabIndex=-1,s.dataset.calendarDay=n,s.setAttribute("aria-pressed",n===l?"true":"false"),s.setAttribute("aria-label",e.toLocaleDateString(r,{weekday:"long",day:"numeric",month:"long",year:"numeric"})),m(e)&&s.classList.add("mw-calendar-weekend"),a||e.getMonth()===t||s.classList.add("mw-calendar-adjacent"),n===d(c)&&(s.classList.add("mw-calendar-today"),s.setAttribute("aria-current","date")),n===l&&s.classList.add("mw-selected");const i=document.createElement("span");i.className="mw-calendar-date",i.textContent=e.getDate(),s.appendChild(i);const u=[].concat(o[n]||[]);if(u.length){const e=document.createElement("span");e.className="mw-calendar-dots",u.forEach(function(t){const n=document.createElement("span");n.className=t?"mw-calendar-dot mw-calendar-dot-"+t:"mw-calendar-dot",e.appendChild(n)}),s.appendChild(e)}return s}function f(){const e=s.getMonth(),o=a?7:42,c=i(a?s:new Date(s.getFullYear(),e,1));t.innerHTML="",function(){const e=i(new Date);for(let n=0;n<7;n++){const n=document.createElement("div");n.className="mw-calendar-weekday",m(e)&&n.classList.add("mw-calendar-weekend"),n.setAttribute("aria-hidden","true"),n.textContent=e.toLocaleDateString(r,{weekday:"short"}),t.appendChild(n),e.setDate(e.getDate()+1)}}();for(let n=0;n<o;n++)t.appendChild(w(c,e)),c.setDate(c.getDate()+1);n&&(n.textContent=u()),function(){const e=t.querySelector(".mw-selected")||t.querySelector(".mw-calendar-today")||t.querySelector(".mw-calendar-day:not(.mw-calendar-adjacent)")||t.querySelector(".mw-calendar-day");e&&(e.tabIndex=0)}()}e.addEventListener("click",function(n){const o=n.target.closest("[data-calendar-nav]");if(o)return r=parseInt(o.dataset.calendarNav,10),a?s.setDate(s.getDate()+7*r):s=new Date(s.getFullYear(),s.getMonth()+r,1),void f();var r;const c=n.target.closest(".mw-calendar-day");var i;c&&!c.disabled&&(i=c.dataset.calendarDay,l=i===l?"":i,t.querySelectorAll(".mw-calendar-day").forEach(function(e){const t=e.dataset.calendarDay===l;e.classList.toggle("mw-selected",t),e.setAttribute("aria-pressed",t?"true":"false")}),e.dispatchEvent(new CustomEvent("mw-calendar-select",{bubbles:!0,detail:{date:l}})))}),t.addEventListener("keydown",function(e){const n={ArrowLeft:-1,ArrowRight:1,ArrowUp:-7,ArrowDown:7}[e.key];if(!n)return;const a=e.target.closest(".mw-calendar-day");if(!a)return;e.preventDefault();const o=new Date(a.dataset.calendarDay+"T00:00:00");o.setDate(o.getDate()+n),function(e){const n=d(e);let a=t.querySelector('[data-calendar-day="'+n+'"]');a||(s=new Date(e),f(),a=t.querySelector('[data-calendar-day="'+n+'"]')),a&&(t.querySelectorAll(".mw-calendar-day").forEach(function(e){e.tabIndex=-1}),a.tabIndex=0,a.focus())}(o)}),f()}["wheel","touchstart"].forEach(e=>{window.addEventListener(e,()=>o=null,{passive:!0})}),window.mwOpenModal=e=>d(document.getElementById(e)),window.mwCloseModal=e=>{var t;(t=document.getElementById(e))&&("DIALOG"===t.tagName?t.close():t.classList.remove("mw-modal-open"))},window.mwRefreshColorSwatches=n}();
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "maverick-wave",
3
- "version": "5.12.0",
3
+ "version": "5.13.0",
4
4
  "config": {
5
- "version_short": "5.12"
5
+ "version_short": "5.13"
6
6
  },
7
7
  "description": "A lightweight, modern CSS framework for building responsive websites with elegance and speed.",
8
8
  "main": "src/js/main.js",
package/src/js/main.js CHANGED
@@ -33,6 +33,7 @@
33
33
  initTabs();
34
34
  initAlerts();
35
35
  initLocalhostIndicator();
36
+ warnMissingViewportFit();
36
37
  initFormSliders();
37
38
  initModals();
38
39
  initHeaderLoginButton();
@@ -287,17 +288,42 @@
287
288
  if (nextBtn)
288
289
  nextBtn.addEventListener('click', () => goToSlide(current + 1));
289
290
 
290
- // Optional: Swipe support
291
+ // Optional: Swipe support. The dominant axis decides, not the horizontal
292
+ // distance alone - a finger scrolling the page down drifts sideways as it
293
+ // goes, and 50px of that is not a swipe. Passive because neither handler
294
+ // cancels the gesture, and without the flag the browser waits for them
295
+ // before it may scroll.
291
296
  let startX = 0;
297
+ let startY = 0;
298
+ let swiping = false;
299
+
292
300
  track.addEventListener(
293
301
  'touchstart',
294
- (e) => (startX = e.touches[0].clientX)
302
+ (e) => {
303
+ // A second finger is a pinch or a two-hand hold, never a swipe
304
+ swiping = e.touches.length === 1;
305
+ if (!swiping) return;
306
+ startX = e.touches[0].clientX;
307
+ startY = e.touches[0].clientY;
308
+ },
309
+ { passive: true }
310
+ );
311
+
312
+ track.addEventListener('touchcancel', () => (swiping = false));
313
+
314
+ track.addEventListener(
315
+ 'touchend',
316
+ (e) => {
317
+ if (!swiping) return;
318
+ swiping = false;
319
+ const deltaX = e.changedTouches[0].clientX - startX;
320
+ const deltaY = e.changedTouches[0].clientY - startY;
321
+ if (Math.abs(deltaX) < 50) return;
322
+ if (Math.abs(deltaX) <= Math.abs(deltaY)) return;
323
+ goToSlide(current + (deltaX < 0 ? 1 : -1));
324
+ },
325
+ { passive: true }
295
326
  );
296
- track.addEventListener('touchend', (e) => {
297
- const delta = e.changedTouches[0].clientX - startX;
298
- if (delta > 50) goToSlide(current - 1);
299
- if (delta < -50) goToSlide(current + 1);
300
- });
301
327
 
302
328
  goToSlide(0);
303
329
  }
@@ -674,7 +700,11 @@
674
700
 
675
701
  const update = () => {
676
702
  queued = false;
677
- const viewport = window.innerHeight;
703
+ // The layout viewport, not innerHeight: on a phone that one is the visual
704
+ // viewport and grows by up to a hundred pixels as the URL bar slides away
705
+ // mid-scroll, which steps every layer at once. clientHeight only moves on
706
+ // a real resize.
707
+ const viewport = document.documentElement.clientHeight;
678
708
  // Every read before the first write, or each write makes the next read
679
709
  // lay the page out again - a reflow per layer per frame.
680
710
  const values = layers.map((layer) => progress(layer, viewport));
@@ -839,7 +869,8 @@
839
869
  }
840
870
  }
841
871
  });
842
- }, 100)
872
+ }, 100),
873
+ { passive: true }
843
874
  );
844
875
  }
845
876
 
@@ -975,26 +1006,42 @@
975
1006
  }
976
1007
 
977
1008
  // ===== Utility Functions =====
1009
+ function isLocalhost() {
1010
+ const host = window.location.hostname;
1011
+ return (
1012
+ host === 'localhost' || host === '127.0.0.1' || host.includes('192.168.')
1013
+ );
1014
+ }
1015
+
978
1016
  function initLocalhostIndicator() {
979
1017
  const activated = document.querySelector(
980
1018
  '.mw-localhost-indicator-activated'
981
1019
  );
982
1020
  const header = document.querySelector('.mw-header');
983
1021
 
984
- if (activated) {
985
- const isLocalhost =
986
- window.location.hostname === 'localhost' ||
987
- window.location.hostname === '127.0.0.1' ||
988
- window.location.hostname.includes('192.168.');
989
-
990
- if (isLocalhost) {
991
- const indicator = document.createElement('div');
992
- indicator.className = 'mw-localhost-indicator-pulse';
993
- header.prepend(indicator);
994
- }
1022
+ if (activated && isLocalhost()) {
1023
+ const indicator = document.createElement('div');
1024
+ indicator.className = 'mw-localhost-indicator-pulse';
1025
+ header.prepend(indicator);
995
1026
  }
996
1027
  }
997
1028
 
1029
+ // iOS resolves every safe-area inset to 0 without it, so the gutter, the nav
1030
+ // panel and the modals lose their cutout budget - silently, hence the warning.
1031
+ function warnMissingViewportFit() {
1032
+ if (!isLocalhost()) return;
1033
+
1034
+ const meta = document.querySelector('meta[name="viewport"]');
1035
+ const content = meta ? meta.getAttribute('content') || '' : '';
1036
+ if (/viewport-fit\s*=\s*cover/.test(content)) return;
1037
+
1038
+ console.warn(
1039
+ 'MaverickWave: <meta name="viewport"> is missing viewport-fit=cover. ' +
1040
+ 'env(safe-area-inset-*) resolves to 0 without it, so notched phones ' +
1041
+ 'lose the gutter, the nav panel and the modal padding that expect it.'
1042
+ );
1043
+ }
1044
+
998
1045
  // ===== Form Sliders =====
999
1046
  function initFormSliders() {
1000
1047
  document.querySelectorAll('.mw-slider-container').forEach((wrapper) => {
@@ -267,5 +267,6 @@
267
267
  WCAG 2.5.8 puts the floor at 24px, and that is a floor, not a goal.
268
268
  <code>mw-btn-mini</code> keeps its 18px circle - it sits in tag rows and table
269
269
  cells where a bigger one would shift the layout - and grows its hit area to
270
- 28px instead. Nothing to switch on; resize this page or open it on a phone.
270
+ 28px instead, as does a kanban action button on its 32px box. Nothing to
271
+ switch on; resize this page or open it on a phone.
271
272
  </p>
@@ -56,7 +56,8 @@
56
56
  &lt;html lang="en"&gt;
57
57
  &lt;head&gt;
58
58
  &lt;meta charset="UTF-8"&gt;
59
- &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
59
+ &lt;!-- viewport-fit=cover is what makes env(safe-area-inset-*) resolve on a notched phone --&gt;
60
+ &lt;meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"&gt;
60
61
  &lt;title&gt;My MaverickWave Project&lt;/title&gt;
61
62
  &lt;!-- Framework CSS --&gt;
62
63
  &lt;link rel="stylesheet" href="path/to/maverick-wave.min.css"&gt;
@@ -200,9 +201,57 @@ body {
200
201
  <i class="fas fa-exclamation-triangle mw-mr-1"></i>
201
202
  Without <code>base</code> every component renders without colors - it
202
203
  carries the <code>:root</code> custom properties. A subset like the
203
- one above compiles to roughly 98 kB raw / 15 kB gzipped, against 147
204
- kB / 22.5 kB for the full build.
204
+ one above compiles to roughly 113 kB raw / 17 kB gzipped, against 281
205
+ kB / 40 kB for the full build.
205
206
  </p>
207
+
208
+ <h5 class="mw-mt-4">Mixins and Functions</h5>
209
+ <p>
210
+ <code>abstracts</code> brings these into your own rules. They exist on
211
+ the SCSS path only - the compiled CSS carries none of them.
212
+ </p>
213
+ <ul class="mw-list">
214
+ <li>
215
+ <code>fluid($min, $max, $from: 'xs', $to: 'md')</code> - a clamp
216
+ between two sizes. Each end is a key of <code>$font-sizes</code> or
217
+ a plain rem/px length, so a hero that does not sit on the scale
218
+ still gets one. The rem term is the point: a vw-only clamp ignores
219
+ the reader's font size. The default range stops at <code>md</code>,
220
+ a headline passes <code>$to: 'xl'</code>.
221
+ </li>
222
+ <li>
223
+ <code>media-up($bp)</code> / <code>media-down($bp)</code> - the
224
+ <code>$breakpoints</code> map as a query.
225
+ </li>
226
+ <li>
227
+ <code>touch-context($bp: 'md')</code> - coarse pointer
228
+ <em>or</em> narrow viewport, the condition every target-size rule in
229
+ the framework hangs under.
230
+ </li>
231
+ <li>
232
+ <code>touch-floor($size: 2.75rem)</code> - that condition plus a
233
+ <code>min-height</code>. Not for a link inside a sentence: WCAG
234
+ 2.5.8 exempts those, and a <code>min-height</code> there pushes the
235
+ lines of the paragraph apart.
236
+ </li>
237
+ <li>
238
+ <code>hit-area($grow: 6px)</code> - grows the hit area through
239
+ <code>::after</code> without touching the silhouette, for a control
240
+ whose size is the design. Neighbours need a gap of at least twice
241
+ <code>$grow</code>, or two hit areas overlap.
242
+ </li>
243
+ </ul>
244
+ <pre
245
+ class="mw-code-block"
246
+ ><code>@use 'maverick-wave/src/scss/abstracts' as *;
247
+
248
+ .hero h1 {
249
+ font-size: fluid(1.95rem, 4.2rem, $to: 'xl');
250
+ }
251
+
252
+ .my-close-button {
253
+ @include touch-floor;
254
+ }</code></pre>
206
255
  </div>
207
256
  </div>
208
257
 
@@ -53,10 +53,13 @@
53
53
  element the page content is a <strong>sibling</strong> of - the section around
54
54
  a hero, not the hero itself - because that is what sticky and the sibling
55
55
  selector need. Everything after it gets the page background and a layer above,
56
- or the pinned picture shows through. Pair it with
57
- <code>mw-header-reveal</code> on the header and the page opens on the picture
58
- alone: the bar stays away for the first 130px of scroll and has ridden in by
59
- 420px.
56
+ or the pinned picture shows through. The section right after it also gets an
57
+ upward shadow, so it reads as sliding over the picture rather than cutting it
58
+ off. That one carries fixed values instead of the elevation ramp: it lands on
59
+ the picture, not on a theme surface, and the light theme's shadows are cut for
60
+ paper and vanish there. Pair it with <code>mw-header-reveal</code> on the
61
+ header and the page opens on the picture alone: the bar stays away for the
62
+ first 130px of scroll and has ridden in by 420px.
60
63
  </p>
61
64
  <p class="mw-text-muted mw-text-measure mw-mb-6">
62
65
  Add <code>mw-parallax-rise</code> next to it and the picture lifts by
@@ -73,7 +76,7 @@
73
76
  </h4>
74
77
  <p class="mw-text-muted mw-text-measure mw-mb-6">
75
78
  The band before the components section on this page: it keeps its place in the
76
- flow and moves at a quarter of your scrolling, so picture and text are still
79
+ flow and moves at a fifth of your scrolling, so picture and text are still
77
80
  going while the next section closes in from below. Pinning is the same move
78
81
  with the tempo at zero.
79
82
  <code>--mw-parallax-slow-travel</code> (<code>45vh</code>) is the dial and
@@ -46,7 +46,7 @@
46
46
  </p>
47
47
  <ul>
48
48
  <li>Built with SCSS for better organization and maintainability</li>
49
- <li>Optimized file size (under 30KB minified and gzipped)</li>
49
+ <li>Optimized file size (about 40KB minified and gzipped)</li>
50
50
  <li>
51
51
  Compatible with all modern browsers (Chrome, Firefox, Safari, Edge)
52
52
  </li>
@@ -3,6 +3,9 @@
3
3
  The heading scale, the body text and everything the framework sets on bare
4
4
  elements. Headings carry <code>text-wrap: balance</code> and a touch of
5
5
  negative tracking, paragraphs <code>text-wrap: pretty</code>.
6
+ <code>h1</code> and <code>h2</code> interpolate between 375px and 768px
7
+ instead of stepping at a breakpoint - a phone and a desktop keep the sizes
8
+ they had, the widths in between grow with the viewport.
6
9
  </p>
7
10
 
8
11
  <div class="mw-grid-2-lg">
@@ -1,5 +1,7 @@
1
1
  @use 'sass:list';
2
2
  @use 'sass:map';
3
+ @use 'sass:math';
4
+ @use 'sass:meta';
3
5
  @use 'variables' as v;
4
6
 
5
7
  // Function to get a color from the colors map
@@ -12,6 +14,54 @@
12
14
  @return map.get(v.$font-sizes, $key);
13
15
  }
14
16
 
17
+ // Interpolates between two sizes, clamped at both ends. Each end is a key of
18
+ // $font-sizes or a plain rem/px length, because a hero rarely sits on the scale
19
+ // and the alternative is a hand-written clamp - which is where the vw-only form
20
+ // creeps back in. The default range stops at 'md' because that is where the
21
+ // stepped sizes it replaces reached their largest value - running it to 'xl'
22
+ // would shrink every heading on a desktop, so a headline passes $to itself.
23
+ // Only the slope is in vw: a size in vw alone ignores the reader's font size and
24
+ // stops responding to zoom, the rem term is what keeps that alive.
25
+ @function fluid($min, $max, $from: 'xs', $to: 'md') {
26
+ $min-rem: _to-rem($min, 'min');
27
+ $max-rem: _to-rem($max, 'max');
28
+ $vw-from: math.div(map.get(v.$breakpoints, $from), 16px);
29
+ $vw-to: math.div(map.get(v.$breakpoints, $to), 16px);
30
+
31
+ $slope: math.div($max-rem - $min-rem, $vw-to - $vw-from);
32
+ $intercept: $min-rem - $slope * $vw-from;
33
+
34
+ @return clamp(
35
+ #{$min-rem}rem,
36
+ #{_round4($intercept)}rem + #{_round4($slope * 100)}vw,
37
+ #{$max-rem}rem
38
+ );
39
+ }
40
+
41
+ @function _to-rem($value, $end) {
42
+ @if meta.type-of($value) == 'string' {
43
+ $mapped: map.get(v.$font-sizes, $value);
44
+ @if $mapped == null {
45
+ @error "fluid(): #{$end} '#{$value}' is not a key of $font-sizes.";
46
+ }
47
+ @return math.div($mapped, 1rem);
48
+ }
49
+ @if math.is-unitless($value) {
50
+ @error "fluid(): #{$end} #{$value} needs a unit - 1.95rem or 31px.";
51
+ }
52
+ @if math.unit($value) == 'rem' {
53
+ @return math.div($value, 1rem);
54
+ }
55
+ @if math.unit($value) == 'px' {
56
+ @return math.div($value, 16px);
57
+ }
58
+ @error "fluid(): #{$end} must be rem or px, got #{$value}.";
59
+ }
60
+
61
+ @function _round4($n) {
62
+ @return math.div(math.round($n * 10000), 10000);
63
+ }
64
+
15
65
  // Function to get a font weight from the font-weights map
16
66
  @function font-weight($key) {
17
67
  @return map.get(v.$font-weights, $key);
@@ -149,6 +149,39 @@
149
149
  -webkit-tap-highlight-color: transparent;
150
150
  }
151
151
 
152
+ // Coarse *or* narrow, because a tablet in landscape is wide and still
153
+ // finger-operated. Named so a consumer can put their own control under the same
154
+ // condition instead of guessing it.
155
+ @mixin touch-context($breakpoint: 'md') {
156
+ @media (pointer: coarse), (width <= #{breakpoint($breakpoint)}) {
157
+ @content;
158
+ }
159
+ }
160
+
161
+ // WCAG 2.5.8 floor for a control that is a line of text under a pointer and a
162
+ // target under a thumb. Not for a link inside a sentence - those are exempt, and
163
+ // a min-height there pushes the lines of the paragraph apart.
164
+ @mixin touch-floor($size: 2.75rem) {
165
+ @include touch-context {
166
+ min-height: $size;
167
+ @content;
168
+ }
169
+ }
170
+
171
+ // Grows the hit area without touching the silhouette - for a control whose size
172
+ // is the design: a 9px gallery dot, an 18px mini button. Neighbours need a gap
173
+ // of at least twice $grow, or two hit areas overlap and a tap lands on the one
174
+ // next door. Takes ::after, so it is out for anything carrying a `data-tooltip`.
175
+ @mixin hit-area($grow: 6px) {
176
+ position: relative;
177
+
178
+ &::after {
179
+ content: '';
180
+ position: absolute;
181
+ inset: -#{$grow};
182
+ }
183
+ }
184
+
152
185
  // One shape for every scroll container and the page itself
153
186
  @mixin scrollbar($thumb: var(--mw-text-muted-color), $track: transparent) {
154
187
  scrollbar-width: thin;
@@ -44,31 +44,14 @@
44
44
  letter-spacing: -0.01em;
45
45
  }
46
46
 
47
- // Two steps, not one. With the single jump at 576px, everything between there
48
- // and a tablet kept the full desktop headline - 3rem of h1 across a 690px
49
- // column is a third of the screen before the first line of copy.
47
+ // The clamp ends where the two steps it replaces ended, so a phone and a
48
+ // desktop are unchanged and only the widths between them move.
50
49
  h1 {
51
- font-size: font-size('3xl');
52
-
53
- @include media-up('sm') {
54
- font-size: font-size('4xl');
55
- }
56
-
57
- @include media-up('md') {
58
- font-size: font-size('5xl');
59
- }
50
+ font-size: fluid('3xl', '5xl');
60
51
  }
61
52
 
62
53
  h2 {
63
- font-size: font-size('2xl');
64
-
65
- @include media-up('sm') {
66
- font-size: font-size('3xl');
67
- }
68
-
69
- @include media-up('md') {
70
- font-size: font-size('4xl');
71
- }
54
+ font-size: fluid('2xl', '4xl');
72
55
  }
73
56
 
74
57
  h3 {
@@ -187,7 +187,7 @@
187
187
  }
188
188
 
189
189
  // The floor a thumb needs, over the visual size above
190
- @media (pointer: coarse), (width <= #{breakpoint('md')}) {
190
+ @include touch-context {
191
191
  --mw-btn-icon-size: 2.75rem;
192
192
 
193
193
  &.mw-btn-sm {
@@ -9,21 +9,29 @@
9
9
  border-radius: radius('md');
10
10
  overflow: hidden;
11
11
 
12
+ // The slider caps at 600px, so past a tablet the viewport no longer says how
13
+ // much room it has. Named, so a query cannot resolve to a container above.
14
+ container: mw-slider / inline-size;
15
+
12
16
  // Heights variables for consistency
13
17
  $height-lg: 370px;
14
18
  $height-md: 320px;
15
19
  $height-sm: 250px;
16
20
 
21
+ // The slider widths the old viewport steps landed on, not the breakpoints
22
+ $wide: 560px;
23
+ $mid: 420px;
24
+
17
25
  &-base {
18
26
  position: relative;
19
27
  width: 100%;
20
28
  height: $height-sm;
21
29
 
22
- @include media-up('sm') {
30
+ @container mw-slider (width >= #{$mid}) {
23
31
  height: $height-md;
24
32
  }
25
33
 
26
- @include media-up('md') {
34
+ @container mw-slider (width >= #{$wide}) {
27
35
  height: $height-lg;
28
36
  }
29
37
  }
@@ -43,11 +51,11 @@
43
51
  height: $height-sm;
44
52
  pointer-events: none;
45
53
 
46
- @include media-up('sm') {
54
+ @container mw-slider (width >= #{$mid}) {
47
55
  height: $height-md;
48
56
  }
49
57
 
50
- @include media-up('md') {
58
+ @container mw-slider (width >= #{$wide}) {
51
59
  height: $height-lg;
52
60
  }
53
61
  }
@@ -86,11 +94,11 @@
86
94
  z-index: z-index('menu');
87
95
  width: 100%;
88
96
 
89
- @include media-up('sm') {
97
+ @container mw-slider (width >= #{$mid}) {
90
98
  grid-template-columns: repeat(3, 1fr);
91
99
  }
92
100
 
93
- @include media-up('md') {
101
+ @container mw-slider (width >= #{$wide}) {
94
102
  gap: spacing('3');
95
103
  padding: spacing('3');
96
104
  }
@@ -106,11 +114,11 @@
106
114
  z-index: z-index('menu');
107
115
  width: 100%;
108
116
 
109
- @include media-up('sm') {
117
+ @container mw-slider (width >= #{$mid}) {
110
118
  grid-template-columns: repeat(2, 1fr);
111
119
  }
112
120
 
113
- @include media-up('md') {
121
+ @container mw-slider (width >= #{$wide}) {
114
122
  gap: spacing('3');
115
123
  padding: spacing('3');
116
124
  }
@@ -257,7 +257,7 @@
257
257
  // A menu opened by a thumb needs rows a thumb can hit, and the room to show
258
258
  // them - so it takes the trigger's full width rather than a 200px column
259
259
  // floating beside it.
260
- @media (pointer: coarse), (width <= #{breakpoint('sm')}) {
260
+ @include touch-context('sm') {
261
261
  .mw-dropdown-menu {
262
262
  min-width: max(100%, 240px);
263
263
  }