From 18ed9f668165c96d577f6394421307c76574e1d9 Mon Sep 17 00:00:00 2001 From: Simon Foster Date: Thu, 9 Apr 2026 21:49:32 +0100 Subject: [PATCH 1/6] [feat] add profile image and adjust layout --- index.css | 28 +++++++++++++++++---- index.html | 60 ++++++++++++++++++++++++++++++++------------- public/profile.jpg | Bin 0 -> 155735 bytes public/profile.png | Bin 0 -> 531092 bytes 4 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 public/profile.jpg create mode 100644 public/profile.png diff --git a/index.css b/index.css index 2aa8848..26c6e38 100644 --- a/index.css +++ b/index.css @@ -218,24 +218,42 @@ /* Hero Section */ .hero { - @apply min-h-[100svh] relative flex flex-col justify-center items-center text-center pt-[120px] pb-20; + @apply min-h-[100svh] relative flex flex-col justify-center items-center pt-[100px] lg:pt-[120px] pb-20; } .hero h1 { - @apply text-5xl md:text-8xl mb-8 md:mb-10; + @apply text-5xl md:text-6xl lg:text-7xl mb-8 lg:mb-8 text-center lg:text-left; } .hero p { - @apply text-lg md:text-2xl text-text-dim max-w-[800px] mx-auto; + @apply text-lg lg:text-2xl text-text-dim max-w-[800px] text-center lg:text-left mx-auto lg:mx-0; text-wrap: balance; } .role-badge { - @apply inline-block px-6 py-2 bg-card-bg border border-glass-border rounded-full text-[0.9rem] font-semibold uppercase tracking-widest mb-8 text-accent-primary; + @apply inline-block px-6 py-2 bg-card-bg border border-glass-border rounded-full text-[0.9rem] font-semibold uppercase tracking-widest mb-8 text-accent-primary text-center; } .hero-btns { - @apply flex flex-col sm:flex-row gap-4 sm:gap-6 justify-center mt-12 md:mt-20; + @apply flex flex-col sm:flex-row gap-4 sm:gap-6 justify-center lg:justify-start mt-10 lg:mt-16; + } + + .hero-image-wrapper { + @apply w-56 h-56 md:w-72 md:h-72 lg:w-[260px] lg:h-[260px] shrink-0 relative mx-auto lg:mx-0; + } + + .profile-frame { + @apply w-full h-full rounded-full p-2 border-[1px] border-glass-border relative transition-[var(--transition-slow)] bg-card-bg backdrop-blur-md; + box-shadow: 0 0 30px rgba(0, 212, 255, 0.05); + } + + .profile-frame:hover { + @apply scale-[1.03] border-accent-primary/40 -translate-y-2; + box-shadow: 0 15px 40px rgba(0, 212, 255, 0.2); + } + + .profile-image { + @apply w-full h-full object-cover rounded-full relative z-10; } /* Scroll Indicator */ diff --git a/index.html b/index.html index 971a310..5ac0091 100644 --- a/index.html +++ b/index.html @@ -30,20 +30,42 @@
-
-
Lead Developer • Full-Stack Engineer
-

Simon Foster

-

Technical Leader with a long track record of turning - messy, - outdated, or complex systems into clean, scalable platforms. A hands-on engineer focused on solving real - problems, guiding teams, and delivering solutions that move businesses forward.

-

I’ve led development teams, modernised legacy stacks, and built - scalable - platforms across PHP, JavaScript, and cloud-native environments. I thrive where technical depth meets - practical decision-making and continuous improvement.

-
- View Experience - Get in Touch +
+
+
+ Experienced Full-Stack Engineer + + Technical Leader + + 30 Years of Digital Transformation +
+

Simon Foster

+
+ AWS TypeScript PHP React +
+

Technical Leader with a long track record of + turning + messy, + outdated, or complex systems into clean, scalable platforms. A hands-on engineer focused on solving + real + problems, guiding teams, and delivering solutions that move businesses forward.

+

I’ve led development teams, modernised legacy stacks, and + built + scalable + platforms across PHP, JavaScript, and cloud-native environments. I thrive where technical depth + meets + practical decision-making and continuous improvement.

+ +
+ +
+
+ Simon Foster +
-
-

Experience

-
- -
-
-
-

Lead Developer

- Nov 2022 - Present -
-
Travel Chapter
-

As a hands-on Lead Developer, my main focus is the technical integration and continuation of - two - technologically different business stacks, architecting solutions that ensure seamless data - continuity and zero service disruption. I collaborate closely with wider business - stakeholders - and technical teams to translate complex requirements into scalable roadmaps, focusing on - modernising legacy structures within cloud-native environments using AWS.

-

I manage and mentor a cross-functional development team, fostering a culture of engineering - excellence and continuous improvement. By championing clean coding principles and - establishing - robust CI/CD practices via GitLab, SonarQube and AWS CDK, I ensure the consistent delivery - of - high-quality, maintainable code across our PHP (Symfony) and TypeScript (Node/React) - ecosystems. +

+
+

Strategic Technical Focus

+
+
+

+ Engineering Philosophy +

+

+ I enjoy building things that last, supporting the people who build them, + and making technology feel like an enabler rather than a blocker. +

+
+
+

+ Event-Driven + Architecture +

+

+ Expert in building scalable microservices and decoupled architectures + using AWS (Lambda, SQS, SNS, DynamoDB), Kubernetes, and Docker. +

+
+
+

+ Full-Stack Ecosystems +

+

+ Architecting type-safe backends in Node.js (TypeScript) and PHP + (Symfony/Laravel), paired with React/Next.js frontends. +

+
+
+

+ Engineering Integrity +

+

+ Strict advocate for SOLID, Clean Architecture, and CI/CD; transitioning + to strongly-typed systems to reduce technical debt. +

+
+
+

Design-to-Code

+

+ Bridging the gap between creative vision and execution leveraging tools + like Figma for high-fidelity implementation.

-
- PHP (Symfony) - SQL (MySQL) - TypeScript (Node/React) - AWS (CDK) - Kubernetes - GitLab (CI/CD) - SonarQube - Jira -
+
- -
-
-
-

Lead Developer

- Sept 2021 - Oct 2022 -
-
The Original Cottage Company
-

In this leadership role, I directed a team of developers in close partnership with product - owners - and business analysts to deliver high-impact solutions across customer focused products. - Translating stakeholder requirements into technical reality, ensuring that innovative - features - were delivered in a timely manner without compromising on architectural integrity.

-

I acted as a mentor and collaborator, championing the delivery of consistent, clean code. By - leveraging tools like Bitbucket Pipelines and Jenkins within an AWS Serverless environment, - I - streamlined development workflows to ensure the team met both technical standards and - evolving - business needs with maximum efficiency.

-
- PHP (Symfony) - SQL (MySQL) - JavaScript (React) - AWS Serverless - Bitbucket Pipelines - Jenkins - Jira +
+

Experience

+
+ +
+
+
+

Lead Developer

+ Nov 2022 - Present +
+
Travel Chapter
+

+ As a hands-on Lead Developer, my main focus is the technical + integration and continuation of two technologically different + business stacks, architecting solutions that ensure seamless data + continuity and zero service disruption. I collaborate closely with + wider business stakeholders and technical teams to translate complex + requirements into scalable roadmaps, focusing on modernising legacy + structures within cloud-native environments using AWS. +

+

+ I manage and mentor a cross-functional development team, fostering a + culture of engineering excellence and continuous improvement. By + championing clean coding principles and establishing robust CI/CD + practices via GitLab, SonarQube and AWS CDK, I ensure the consistent + delivery of high-quality, maintainable code across our PHP (Symfony) + and TypeScript (Node/React) ecosystems. +

+
+ PHP (Symfony) + SQL (MySQL) + TypeScript (Node/React) + AWS (CDK) + Kubernetes + GitLab (CI/CD) + SonarQube + Jira +
-
- -
-
-
-

Developer

- May 2019 - Sept 2021 -
-
The Original Cottage Company
-

My primary focus was the continuous improvement of the group’s digital presence, where I - developed new features aimed at optimising the customer journey and driving user engagement. - I - performed critical maintenance across the web estate and worked extensively with the core - booking system API to ensure a high-performance, seamless experience for guests and owners. -

-

Working within a modern PHP and JavaScript stack, I prioritised efficiency and speed, - ensuring - the integration between the frontend platforms and backend services was robust and scalable. - This role was fundamental in refining my expertise in AWS Serverless architecture and - Drupal-based environments.

-
- PHP (Symfony) - SQL (MySQL) - JavaScript (React) - AWS Serverless - Drupal - Jenkins - Bitbucket + +
+
+
+

Lead Developer

+ Sept 2021 - Oct 2022 +
+
The Original Cottage Company
+

+ In this leadership role, I directed a team of developers in close + partnership with product owners and business analysts to deliver + high-impact solutions across customer focused products. Translating + stakeholder requirements into technical reality, ensuring that + innovative features were delivered in a timely manner without + compromising on architectural integrity. +

+

+ I acted as a mentor and collaborator, championing the delivery of + consistent, clean code. By leveraging tools like Bitbucket Pipelines + and Jenkins within an AWS Serverless environment, I streamlined + development workflows to ensure the team met both technical + standards and evolving business needs with maximum efficiency. +

+
+ PHP (Symfony) + SQL (MySQL) + JavaScript (React) + AWS Serverless + Bitbucket Pipelines + Jenkins + Jira +
-
- -
-
-
-

Technical Director / Senior Developer

- Aug 1995 - May 2019 + +
+
+
+

Developer

+ May 2019 - Sept 2021 +
+
The Original Cottage Company
+

+ My primary focus was the continuous improvement of the group’s + digital presence, where I developed new features aimed at optimising + the customer journey and driving user engagement. I performed + critical maintenance across the web estate and worked extensively + with the core booking system API to ensure a high-performance, + seamless experience for guests and owners. +

+

+ Working within a modern PHP and JavaScript stack, I prioritised + efficiency and speed, ensuring the integration between the frontend + platforms and backend services was robust and scalable. This role + was fundamental in refining my expertise in AWS Serverless + architecture and Drupal-based environments. +

+
+ PHP (Symfony) + SQL (MySQL) + JavaScript (React) + AWS Serverless + Drupal + Jenkins + Bitbucket +
-
Cambridge Occupational Analysts (COA Ltd)
-

As Technical Director, I had accountability for the IT department - and was involved with product strategy, bridging the gap between directorial vision and - technical execution. My primary focus was spearheading the digital transformation of the - company’s core product, successfully migrating a legacy SQL Server and Access-based - ecosystem - into a modern, high-performance web application driven by RESTful APIs, Laravel, and - Angular. -

-

I oversaw the maintenance of all existing products and - infrastructure while providing technical mentorship and leadership to the development team. - By - modernising our internal workflows and infrastructure via VMWare and Linux environments, I - ensured high system availability and a scalable foundation for new product development.

+
-
-
- Lead Developer / IT Manager (Jan 2003 - Aug 2010) -

Managed the IT team responsible for the end-to-end design and support of back-office - and - customer-facing web applications. Directed the provision of server and network - infrastructure across Linux and Windows environments.

+ +
+
+
+

Technical Director / Senior Developer

+ Aug 1995 - May 2019
-
- Lead Developer (Jan 1998 - Jan 2003) -

Engineered a new integrated database system for careers reporting, replacing legacy - RISC-OS systems with a connected modern Windows/Linux infrastructure.

+
+ Cambridge Occupational Analysts (COA Ltd)
-
- Software Developer (Aug 1995 - Jan 1998) -

Maintenance and development of internal report-generation systems and desktop - support. -

+

+ As Technical Director, I had accountability for the IT department + and was involved with product strategy, bridging the gap between + directorial vision and technical execution. My primary focus was + spearheading the digital transformation of the company’s core + product, successfully migrating a legacy SQL Server and Access-based + ecosystem into a modern, high-performance web application driven by + RESTful APIs, Laravel, and Angular. +

+

+ I oversaw the maintenance of all existing products and + infrastructure while providing technical mentorship and leadership + to the development team. By modernising our internal workflows and + infrastructure via VMWare and Linux environments, I ensured high + system availability and a scalable foundation for new product + development. +

+ +
+
+ Lead Developer / IT Manager (Jan 2003 - Aug 2010) +

+ Managed the IT team responsible for the end-to-end design + and support of back-office and customer-facing web + applications. Directed the provision of server and network + infrastructure across Linux and Windows environments. +

+
+
+ Lead Developer (Jan 1998 - Jan 2003) +

+ Engineered a new integrated database system for careers + reporting, replacing legacy RISC-OS systems with a connected + modern Windows/Linux infrastructure. +

+
+
+ Software Developer (Aug 1995 - Jan 1998) +

+ Maintenance and development of internal report-generation + systems and desktop support. +

+
-
-
- PHP (Laravel) - Angular (5+) - SQL (SQL Server) - Linux (Apache/NGINX) - VMWare - VB6 / VBA - C/C++ - Windows Server +
+ PHP (Laravel) + Angular (5+) + SQL (SQL Server) + Linux (Apache/NGINX) + VMWare + VB6 / VBA + C/C++ + Windows Server +
-
- -
-
-
-

Small Business Consultant

- Jan 2000 - Dec 2018 -
-
Freelance Consultancy
-

Provided strategic IT support and consultancy to small businesses, with a focus on - modernising - legacy workflows through networking, web design, and hosting solutions. I acted as a - technical - partner, helping clients optimise their existing technology stacks while offering scalable - suggestions for future-proofing their digital presence through cloud technologies.

-
- PHP - MySQL - HTML/CSS - JavaScript - WordPress - Linux / Windows - Cloud Technologies + +
+
+
+

Small Business Consultant

+ Jan 2000 - Dec 2018 +
+
Freelance Consultancy
+

+ Provided strategic IT support and consultancy to small businesses, + with a focus on modernising legacy workflows through networking, web + design, and hosting solutions. I acted as a technical partner, + helping clients optimise their existing technology stacks while + offering scalable suggestions for future-proofing their digital + presence through cloud technologies. +

+
+ PHP + MySQL + HTML/CSS + JavaScript + WordPress + Linux / Windows + Cloud Technologies +
-
-
+
-
-

Education

-
- -
-

University of East Anglia

-

BSc Honours, Computing Science | 1992 - 1995

-

Grade: 2:1

-

Activities: UEA Korfball Club

-
- -
-

Barnsley College

-

A levels | 1990 - 1992

-

Computer Studies (A); Electronic Systems (A); Mathematics with Statistics (C)

-
- -
-

The Foulstone School

-

GCSEs | 1985 - 1990

-

Eight GCSEs: Mathematics (A), Computer Studies (A), Electronics (A), Geography (A), Dual Award - Science (A,B), English (C) and French (C).

+
+

Education

+
+ +
+

+ University of East + Anglia +

+

BSc Honours, Computing Science | 1992 - 1995

+

Grade: 2:1

+

Activities: UEA Korfball Club

+
+ +
+

Barnsley College

+

A levels | 1990 - 1992

+

+ Computer Studies (A); Electronic Systems (A); Mathematics with + Statistics (C) +

+
+ +
+

+ The Foulstone School +

+

GCSEs | 1985 - 1990

+

+ Eight GCSEs: Mathematics (A), Computer Studies (A), Electronics (A), + Geography (A), Dual Award Science (A,B), English (C) and French (C). +

+
-
-
+ -
-

Interests

-
-
-

Korfball

-

Fully qualified Level II coach and regular player. Active in the Norfolk Korfball Association. -

-
-
-

Photography

-

Landscape photographer capturing the beauty of the Norfolk countryside through a professional - lens. -

-
-
-

Cycling

-

Avid cyclist exploring the scenic routes, coastal paths, and quiet lanes of East Anglia.

-
-
-

Pokemon Go

-

Enjoying chasing Pokémon with family on long walks.

+
+

Interests

+
+
+

Korfball

+

+ Fully qualified Level II coach and regular player. Active in the Norfolk + Korfball Association. +

+
+
+

Photography

+

+ Landscape photographer capturing the beauty of the Norfolk countryside + through a professional lens. +

+
+
+

Cycling

+

+ Avid cyclist exploring the scenic routes, coastal paths, and quiet lanes + of East Anglia. +

+
+
+

Pokemon Go

+

Enjoying chasing Pokémon with family on long walks.

+
-
-
+ - - - - + - \ No newline at end of file + + + + diff --git a/main.js b/main.js index bfbe014..ffe724a 100644 --- a/main.js +++ b/main.js @@ -13,19 +13,22 @@ window.addEventListener('scroll', () => { // Reveal Animation on Scroll const revealElements = document.querySelectorAll('[data-reveal]'); -const revealObserver = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.classList.add('active'); - revealObserver.unobserve(entry.target); - } - }); -}, { - threshold: 0.15, - rootMargin: '0px 0px -50px 0px' -}); +const revealObserver = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + entry.target.classList.add('active'); + revealObserver.unobserve(entry.target); + } + }); + }, + { + threshold: 0.15, + rootMargin: '0px 0px -50px 0px', + }, +); -revealElements.forEach(el => { +revealElements.forEach((el) => { revealObserver.observe(el); }); diff --git a/package-lock.json b/package-lock.json index 7fb9e59..31f1c5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1527 +1,1624 @@ { - "name": "sf", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "sf", - "version": "0.0.0", - "devDependencies": { - "@axe-core/playwright": "^4.11.1", - "@playwright/test": "^1.59.1", - "@tailwindcss/vite": "^4.2.2", - "autoprefixer": "^10.4.27", - "postcss": "^8.5.8", - "tailwindcss": "^4.2.2", - "vite": "^8.0.4" - } - }, - "node_modules/@axe-core/playwright": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.1.tgz", - "integrity": "sha512-mKEfoUIB1MkVTht0BGZFXtSAEKXMJoDkyV5YZ9jbBmZCcWDz71tegNsdTkIN8zc/yMi5Gm2kx7Z5YQ9PfWNAWw==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "axe-core": "~4.11.1" - }, - "peerDependencies": { - "playwright-core": ">= 1.0.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@playwright/test": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", - "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.59.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tailwindcss/node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", - "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", - "jiti": "^2.6.1", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.2.2" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", - "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-x64": "4.2.2", - "@tailwindcss/oxide-freebsd-x64": "4.2.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-x64-musl": "4.2.2", - "@tailwindcss/oxide-wasm32-wasi": "4.2.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", - "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", - "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", - "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", - "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", - "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", - "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", - "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", - "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", - "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", - "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", - "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", - "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", - "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.2.2", - "@tailwindcss/oxide": "4.2.2", - "tailwindcss": "4.2.2" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.27", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", - "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001774", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axe-core": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz", - "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.16", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", - "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001786", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001786.tgz", - "integrity": "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.331", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", - "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/playwright": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", - "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.59.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", - "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tailwindcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", - "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/vite": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.5.tgz", - "integrity": "sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true + "name": "sf", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sf", + "version": "0.0.0", + "devDependencies": { + "@axe-core/playwright": "^4.11.1", + "@playwright/test": "^1.59.1", + "@tailwindcss/vite": "^4.2.2", + "autoprefixer": "^10.4.27", + "postcss": "^8.5.8", + "prettier": "^3.8.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "tailwindcss": "^4.2.2", + "vite": "^8.0.4" + } + }, + "node_modules/@axe-core/playwright": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.1.tgz", + "integrity": "sha512-mKEfoUIB1MkVTht0BGZFXtSAEKXMJoDkyV5YZ9jbBmZCcWDz71tegNsdTkIN8zc/yMi5Gm2kx7Z5YQ9PfWNAWw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.11.1" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axe-core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz", + "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", + "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001786", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001786.tgz", + "integrity": "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.2.tgz", + "integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.5.tgz", + "integrity": "sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } } - } } - } } diff --git a/package.json b/package.json index 3083b66..134af4f 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,25 @@ { - "name": "sf", - "version": "0.0.0", - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview", - "test": "playwright test", - "test:accessibility": "playwright test tests/accessibility.spec.js" - }, - "devDependencies": { - "@axe-core/playwright": "^4.11.1", - "@playwright/test": "^1.59.1", - "@tailwindcss/vite": "^4.2.2", - "autoprefixer": "^10.4.27", - "postcss": "^8.5.8", - "tailwindcss": "^4.2.2", - "vite": "^8.0.4" - } + "name": "sf", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test": "playwright test", + "test:accessibility": "playwright test tests/accessibility.spec.js", + "format": "prettier --write ." + }, + "devDependencies": { + "@axe-core/playwright": "^4.11.1", + "@playwright/test": "^1.59.1", + "@tailwindcss/vite": "^4.2.2", + "autoprefixer": "^10.4.27", + "postcss": "^8.5.8", + "prettier": "^3.8.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "tailwindcss": "^4.2.2", + "vite": "^8.0.4" + } } diff --git a/playwright-report/index.html b/playwright-report/index.html index 80035bb..44f20bf 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -1,90 +1,23255 @@ - - - - - - - - - Playwright Test Report - - - - -
- +` + + V.gitDiff + + '\n```'), + p + ); + }, + [T, i, u], + void 0, + ); + return m.jsxs('div', { + className: 'test-result', + children: [ + !!v.length && + m.jsxs(ke, { + header: 'Errors', + children: [ + q && + m.jsx('div', { + style: { + position: 'absolute', + right: '16px', + padding: '10px', + zIndex: 1, + }, + children: m.jsx(_5, { prompt: q }), + }), + v.map((p, E) => { + const b = iv(p, y); + return m.jsxs(m.Fragment, { + children: [ + m.jsx( + Ar, + { code: p }, + 'test-result-error-message-' + E, + ), + b && m.jsx(P5, { diff: b }), + ], + }); + }), + ], + }), + !!u.steps.length && + m.jsxs(ke, { + header: 'Test Steps', + children: [ + m.jsxs('form', { + className: 'subnav-search step-filter', + onSubmit: (p) => p.preventDefault(), + children: [ + Ch(), + m.jsx('input', { + className: + 'form-control subnav-search-input input-contrast width-full', + type: 'search', + spellCheck: !1, + placeholder: 'Filter steps', + 'aria-label': 'Filter steps', + value: D, + onChange: (p) => X(p.target.value), + }), + ], + }), + u.steps.map((p, E) => + m.jsx( + Kh, + { + step: p, + result: u, + test: c, + depth: 0, + filterText: D, + }, + `step-${E}`, + ), + ), + ], + }), + y.map((p, E) => + m.jsx( + xi, + { + id: p.anchors, + children: m.jsx(ke, { + dataTestId: 'test-results-image-diff', + header: `Image mismatch: ${p.name}`, + revealOnAnchorId: p.anchors, + children: m.jsx(Xh, { diff: p }), + }), + }, + `diff-${E}`, + ), + ), + !!f.length && + m.jsx(ke, { + header: 'Screenshots', + revealOnAnchorId: x, + children: f.map((p, E) => + m.jsxs( + xi, + { + id: `attachment-${u.attachments.indexOf(p)}`, + children: [ + m.jsx('a', { + href: Ve(p.path), + children: m.jsx('img', { + className: 'screenshot', + src: Ve(p.path), + }), + }), + m.jsx($u, { attachment: p, result: u }), + ], + }, + `screenshot-${E}`, + ), + ), + }), + !!o.length && + m.jsx(xi, { + id: 'attachment-trace', + children: m.jsx(ke, { + header: 'Traces', + revealOnAnchorId: 'attachment-trace', + children: m.jsxs('div', { + children: [ + m.jsx('a', { + href: Ve(Yh(o)), + children: m.jsx('img', { + className: 'screenshot', + src: Z5, + style: { + width: 192, + height: 117, + marginLeft: 20, + }, + }), + }), + o.map((p, E) => + m.jsx( + $u, + { + attachment: p, + result: u, + linkName: + o.length === 1 + ? 'trace' + : `trace-${E + 1}`, + }, + `trace-${E}`, + ), + ), + ], + }), + }), + }), + !!r.length && + m.jsx(xi, { + id: 'attachment-video', + children: m.jsx(ke, { + header: 'Videos', + revealOnAnchorId: 'attachment-video', + children: r.map((p) => + m.jsxs( + 'div', + { + children: [ + m.jsx('video', { + controls: !0, + children: m.jsx('source', { + src: Ve(p.path), + type: p.contentType, + }), + }), + m.jsx($u, { attachment: p, result: u }), + ], + }, + p.path, + ), + ), + }), + }), + !!h.size && + m.jsx(ke, { + header: 'Attachments', + revealOnAnchorId: A, + dataTestId: 'attachments', + children: [...h].map((p, E) => + m.jsx( + xi, + { + id: `attachment-${u.attachments.indexOf(p)}`, + children: m.jsx($u, { + attachment: p, + result: u, + openInNewTab: p.contentType.startsWith('text/html'), + }), + }, + `attachment-link-${E}`, + ), + ), + }), + m.jsx(ke, { + header: `Executed in Worker #${u.workerIndex}`, + dataTestId: 'worker-test-list', + initialExpanded: !1, + noInsets: !0, + body: () => { + const p = uv(i).get(u.workerIndex) || { tests: [], runs: [] }; + return m.jsx(Zh, { + tests: p.tests, + runs: p.runs, + projectNames: i.json().projectNames, + selectedTestId: c.testId, + }); + }, + }), + ], + }); + }; + function iv(i, c) { + const u = i.split(` +`)[0]; + if (!(!u.includes('toHaveScreenshot') && !u.includes('toMatchSnapshot'))) + return c.find((f) => i.includes(f.name)); + } + function Ih(i, c) { + return i.title.toLowerCase().includes(c.toLowerCase()); + } + function qh(i, c) { + return i.steps.some((u) => Ih(u, c) || qh(u, c)); + } + const Kh = ({ test: i, step: c, result: u, depth: f, filterText: r }) => { + const o = se(); + let h = !1, + y = m.jsx('span', { children: c.title }); + if (r) { + const v = !!r && Ih(c, r), + A = !!r && qh(c, r); + if (!v && !A) return null; + if (((h = A), v)) { + const x = c.title.toLowerCase().split(r.toLowerCase()), + T = []; + let D = 0; + for (let X = 0; X < x.length; X++) + (X && + (T.push( + m.jsx( + 'span', + { + className: 'step-title-highlight', + children: c.title.substring(D, D + r.length), + }, + X, + ), + ), + (D += r.length)), + T.push(x[X]), + (D += x[X].length)); + y = T; + } + } + return m.jsx(V5, { + title: m.jsxs('div', { + 'aria-label': c.title, + className: 'step-title-container', + children: [ + fc( + c.error || c.duration === -1 + ? 'failed' + : c.skipped + ? 'skipped' + : 'passed', + ), + m.jsxs('span', { + className: 'step-title-text', + children: [ + y, + c.count > 1 && + m.jsxs(m.Fragment, { + children: [ + ' ✕ ', + m.jsx('span', { + className: 'test-result-counter', + children: c.count, + }), + ], + }), + c.location && + m.jsxs('span', { + className: 'test-result-path', + children: [ + '— ', + c.location.file, + ':', + c.location.line, + ], + }), + ], + }), + m.jsx('span', { className: 'step-spacer' }), + c.attachments.length > 0 && + m.jsx('a', { + className: 'step-attachment-link', + title: 'reveal attachment', + href: Ve( + il( + { + test: i, + result: u, + anchor: `attachment-${c.attachments[0]}`, + }, + o, + ), + ), + onClick: (v) => { + v.stopPropagation(); + }, + children: Dh(), + }), + m.jsx('span', { + className: 'step-duration', + children: Ta(c.duration), + }), + ], + }), + loadChildren: + c.steps.length || c.snippet + ? () => { + const v = c.snippet + ? [ + m.jsx( + Ar, + { testId: 'test-snippet', code: c.snippet }, + 'line', + ), + ] + : [], + A = c.steps.map((x, T) => + m.jsx( + Kh, + { + step: x, + depth: f + 1, + result: u, + test: i, + filterText: r, + }, + T, + ), + ); + return v.concat(A); + } + : void 0, + depth: f, + expandByDefault: h, + }); + }, + C2 = Symbol('workerLists'); + function uv(i) { + let c = i[C2]; + if (!c) { + const u = new Map(); + for (const f of i.json().files) + for (const r of f.tests) + for (let o = 0; o < r.results.length; o++) { + let h = u.get(r.results[o].workerIndex); + (h || ((h = []), u.set(r.results[o].workerIndex, h)), + h.push({ + test: r, + time: new Date(r.results[o].startTime).valueOf(), + run: o, + })); + } + c = new Map(); + for (const [f, r] of u) + (r.sort((o, h) => o.time - h.time), + c.set(f, { tests: r.map((o) => o.test), runs: r.map((o) => o.run) })); + i[C2] = c; + } + return c; + } + const cv = ({ report: i, test: c, run: u, next: f, prev: r }) => { + const [o, h] = it.useState(u), + y = se(), + v = c.annotations.filter((A) => !A.type.startsWith('_')) ?? []; + return m.jsxs(m.Fragment, { + children: [ + m.jsx(mr, { + title: c.title, + leftSuperHeader: m.jsx('div', { + className: 'test-case-path', + children: c.path.join(' › '), + }), + rightSuperHeader: m.jsxs(m.Fragment, { + children: [ + m.jsx('div', { + className: Ze(!r && 'hidden'), + children: m.jsx(bn, { + href: il({ test: r }, y), + children: '« previous', + }), + }), + m.jsx('div', { style: { width: 10 } }), + m.jsx('div', { + className: Ze(!f && 'hidden'), + children: m.jsx(bn, { + href: il({ test: f }, y), + children: 'next »', + }), + }), + ], + }), + }), + m.jsxs('div', { + className: 'hbox', + style: { lineHeight: '24px' }, + children: [ + m.jsx('div', { + className: 'test-case-location', + children: m.jsxs(or, { + value: `${c.location.file}:${c.location.line}`, + children: [c.location.file, ':', c.location.line], + }), + }), + m.jsx('div', { style: { flex: 'auto' } }), + m.jsx(Qh, { test: c, trailingSeparator: !0 }), + m.jsx('div', { + className: 'test-case-duration', + children: Ta(c.duration), + }), + ], + }), + m.jsx(Uh, { + style: { marginLeft: '6px' }, + projectNames: i.json().projectNames, + activeProjectName: c.projectName, + otherLabels: c.tags, + }), + c.results.length === 0 && + v.length !== 0 && + m.jsx(ke, { + header: 'Annotations', + dataTestId: 'test-case-annotations', + children: v.map((A, x) => m.jsx(O2, { annotation: A }, x)), + }), + m.jsx(X5, { + tabs: + c.results.map((A, x) => ({ + id: String(x), + title: m.jsxs('div', { + style: { display: 'flex', alignItems: 'center' }, + children: [ + fc(A.status), + ' ', + sv(x), + c.results.length > 1 && + m.jsx('span', { + className: 'test-case-run-duration', + children: Ta(A.duration), + }), + ], + }), + render: () => { + const T = A.annotations.filter( + (D) => !D.type.startsWith('_'), + ); + return m.jsxs(m.Fragment, { + children: [ + !!T.length && + m.jsx(ke, { + header: 'Annotations', + dataTestId: 'test-case-annotations', + children: T.map((D, X) => + m.jsx(O2, { annotation: D }, X), + ), + }), + m.jsx(av, { test: c, result: A, report: i }), + ], + }); + }, + })) || [], + selectedTab: String(o), + setSelectedTab: (A) => h(+A), + }), + ], + }); + }; + function O2({ annotation: { type: i, description: c } }) { + return m.jsxs('div', { + className: 'test-case-annotation', + children: [ + m.jsx('span', { style: { fontWeight: 'bold' }, children: i }), + c && m.jsxs(or, { value: c, children: [': ', Di(c)] }), + ], + }); + } + function sv(i) { + return i ? `Retry #${i}` : 'Run'; + } + class fv extends it.Component { + constructor() { + (super(...arguments), (this.state = { error: null, errorInfo: null })); + } + componentDidCatch(c, u) { + this.setState({ error: c, errorInfo: u }); + } + render() { + var c, u, f; + return this.state.error || this.state.errorInfo + ? m.jsxs('div', { + className: 'metadata-view p-3', + children: [ + m.jsx('p', { + children: + 'An error was encountered when trying to render metadata.', + }), + m.jsx('p', { + children: m.jsxs('pre', { + style: { overflow: 'scroll' }, + children: [ + (c = this.state.error) == null ? void 0 : c.message, + m.jsx('br', {}), + (u = this.state.error) == null ? void 0 : u.stack, + m.jsx('br', {}), + (f = this.state.errorInfo) == null + ? void 0 + : f.componentStack, + ], + }), + }), + ], + }) + : this.props.children; + } + } + const rv = (i) => m.jsx(fv, { children: m.jsx(ov, { metadata: i.metadata }) }), + ov = (i) => { + const c = i.metadata, + u = se().has('show-metadata-other') + ? Object.entries(i.metadata).filter(([r]) => !kh.has(r)) + : []; + if (c.ci || c.gitCommit || u.length > 0) + return m.jsxs('div', { + className: 'metadata-view', + children: [ + c.ci && !c.gitCommit && m.jsx(dv, { info: c.ci }), + c.gitCommit && m.jsx(hv, { ci: c.ci, commit: c.gitCommit }), + u.length > 0 && + m.jsxs(m.Fragment, { + children: [ + (c.gitCommit || c.ci) && + m.jsx('div', { className: 'metadata-separator' }), + m.jsx('div', { + className: 'metadata-section metadata-properties', + role: 'list', + children: u.map(([r, o]) => { + const h = + typeof o != 'object' || + o === null || + o === void 0 + ? String(o) + : JSON.stringify(o), + y = + h.length > 1e3 + ? h.slice(0, 1e3) + '…' + : h; + return m.jsx( + 'div', + { + className: 'copyable-property', + role: 'listitem', + children: m.jsxs(or, { + value: h, + children: [ + m.jsx('span', { + style: { + fontWeight: 'bold', + }, + title: r, + children: r, + }), + ': ', + m.jsx('span', { + title: y, + children: Di(y), + }), + ], + }), + }, + r, + ); + }), + }), + ], + }), + ], + }); + }, + dv = ({ info: i }) => { + const c = i.prTitle || `Commit ${i.commitHash}`, + u = i.prHref || i.commitHref; + return m.jsx('div', { + className: 'metadata-section', + role: 'list', + children: m.jsx('div', { + role: 'listitem', + children: m.jsx('a', { + href: Ve(u), + target: '_blank', + rel: 'noopener noreferrer', + title: c, + children: c, + }), + }), + }); + }, + hv = ({ ci: i, commit: c }) => { + const u = (i == null ? void 0 : i.prTitle) || c.subject, + f = (i == null ? void 0 : i.prHref) || (i == null ? void 0 : i.commitHref), + r = ` <${c.author.email}>`, + o = `${c.author.name}${r}`, + h = Intl.DateTimeFormat(void 0, { dateStyle: 'medium' }).format( + c.committer.time, + ), + y = Intl.DateTimeFormat(void 0, { + dateStyle: 'full', + timeStyle: 'long', + }).format(c.committer.time); + return m.jsxs('div', { + className: 'metadata-section', + role: 'list', + children: [ + m.jsxs('div', { + role: 'listitem', + children: [ + f && + m.jsx('a', { + href: Ve(f), + target: '_blank', + rel: 'noopener noreferrer', + title: u, + children: u, + }), + !f && m.jsx('span', { title: u, children: u }), + ], + }), + m.jsxs('div', { + role: 'listitem', + className: 'hbox', + children: [ + m.jsx('span', { className: 'mr-1', children: o }), + m.jsxs('span', { title: y, children: [' on ', h] }), + ], + }), + ], + }); + }, + kh = new Set(['ci', 'gitCommit', 'gitDiff', 'actualWorkers']), + mv = (i) => { + const c = Object.entries(i).filter(([u]) => !kh.has(u)); + return !i.ci && !i.gitCommit && !c.length; + }, + gv = ({ files: i, expandedFiles: c, setExpandedFiles: u, projectNames: f }) => { + const r = it.useMemo(() => { + const o = []; + let h = 0; + for (const y of i) + ((h += y.tests.length), o.push({ file: y, defaultExpanded: h < 200 })); + return o; + }, [i]); + return m.jsx(m.Fragment, { + children: + r.length > 0 + ? r.map(({ file: o, defaultExpanded: h }) => + m.jsx( + Vh, + { + file: o, + projectNames: f, + isFileExpanded: (y) => { + const v = c.get(y); + return v === void 0 ? h : !!v; + }, + setFileExpanded: (y, v) => { + const A = new Map(c); + (A.set(y, v), u(A)); + }, + }, + `file-${o.fileId}`, + ), + ) + : m.jsx('div', { + className: 'chip-header test-file-no-files', + children: 'No tests found', + }), + }); + }, + D2 = ({ + report: i, + filteredStats: c, + metadataVisible: u, + toggleMetadataVisible: f, + errorsVisible: r, + setErrorsVisible: o, + }) => { + if (!i) return null; + const h = i.projectNames.length === 1 && !!i.projectNames[0], + y = !h && !c, + v = + !mv(i.metadata) && + m.jsxs('div', { + className: Ze( + 'metadata-toggle', + !y && 'metadata-toggle-second-line', + ), + role: 'button', + onClick: f, + title: u ? 'Hide metadata' : 'Show metadata', + children: [u ? Mi() : Sa(), 'Metadata'], + }), + A = m.jsxs('div', { + className: 'test-file-header-info', + children: [ + h && + m.jsxs('div', { + 'data-testid': 'project-name', + children: ['Project: ', i.projectNames[0]], + }), + c && + m.jsxs('div', { + 'data-testid': 'filtered-tests-count', + children: [ + 'Filtered: ', + c.total, + ' ', + !!c.total && '(' + Ta(c.duration) + ')', + ], + }), + y && v, + ], + }), + x = m.jsxs(m.Fragment, { + children: [ + m.jsx('div', { + 'data-testid': 'overall-time', + style: { marginRight: '10px' }, + children: i ? new Date(i.startTime).toLocaleString() : '', + }), + m.jsxs('div', { + 'data-testid': 'overall-duration', + children: ['Total time: ', Ta(i.duration ?? 0)], + }), + ], + }); + return m.jsxs(m.Fragment, { + children: [ + m.jsx(mr, { + title: i.options.title, + leftSuperHeader: A, + rightSuperHeader: x, + }), + !y && v, + u && m.jsx(rv, { metadata: i.metadata }), + !!i.errors.length && + m.jsx(gr, { + header: 'Errors', + dataTestId: 'report-errors', + expanded: r, + setExpanded: o, + children: i.errors.map((T, D) => + m.jsx(Ar, { code: T }, 'test-report-error-message-' + D), + ), + }), + ], + }); + }, + Jh = (i) => { + const c = Math.round(i / 1e3), + u = Math.floor(c / 60), + f = c % 60; + return u === 0 ? `${f}s` : `${u}m ${f}s`; + }, + Av = ({ entries: i }) => { + const f = Math.max(...i.map((j) => j.label.length)) * 10, + o = { + top: 20, + right: 20, + bottom: 40, + left: Math.min(800 * 0.5, Math.max(50, f)), + }, + h = 800 - o.left - o.right, + y = Math.min(...i.map((j) => j.startTime)), + v = Math.max(...i.map((j) => j.startTime + j.duration)); + let A, x; + const T = v - y; + T < 60 * 1e3 + ? ((A = 10 * 1e3), (x = !0)) + : T < 300 * 1e3 + ? ((A = 30 * 1e3), (x = !0)) + : T < 1800 * 1e3 + ? ((A = 300 * 1e3), (x = !1)) + : ((A = 600 * 1e3), (x = !1)); + const D = Math.ceil(y / A) * A, + X = (j, Y) => { + const z = new Date(j).toLocaleTimeString(void 0, { + hour: '2-digit', + minute: '2-digit', + second: x ? '2-digit' : void 0, + }); + if (Y) return z; + if (z.endsWith(' AM') || z.endsWith(' PM')) return z.slice(0, -3); + }, + p = (v - y) * 1.1, + E = Math.ceil(p / A) * A, + b = h / E, + R = 20, + N = 8, + V = i.length * (R + N), + F = []; + for (let j = D; j <= y + E; j += A) { + const Y = j - y; + F.push({ x: Y * b, label: X(j, j === D) }); + } + const H = V + o.top + o.bottom; + return m.jsx('svg', { + viewBox: `0 0 800 ${H}`, + preserveAspectRatio: 'xMidYMid meet', + style: { width: '100%', height: 'auto' }, + role: 'img', + children: m.jsxs('g', { + transform: `translate(${o.left}, ${o.top})`, + role: 'presentation', + children: [ + F.map(({ x: j, label: Y }, z) => + m.jsxs( + 'g', + { + 'aria-hidden': 'true', + children: [ + m.jsx('line', { + x1: j, + y1: 0, + x2: j, + y2: V, + stroke: 'var(--color-border-muted)', + strokeWidth: '1', + }), + m.jsx('text', { + x: j, + y: V + 20, + textAnchor: 'middle', + dominantBaseline: 'middle', + fontSize: '12', + fill: 'var(--color-fg-muted)', + children: Y, + }), + ], + }, + z, + ), + ), + i.map((j, Y) => { + const z = j.startTime - y, + I = j.duration * b, + k = z * b, + nt = Y * (R + N), + P = [ + 'var(--color-scale-blue-2)', + 'var(--color-scale-blue-3)', + 'var(--color-scale-blue-4)', + ], + st = P[Y % P.length]; + return m.jsxs( + 'g', + { + role: 'listitem', + 'aria-label': j.tooltip, + children: [ + m.jsx('rect', { + className: 'gantt-bar', + x: k, + y: nt, + width: I, + height: R, + fill: st, + rx: '2', + tabIndex: 0, + children: m.jsx('title', { + children: j.tooltip, + }), + }), + m.jsx('text', { + x: k + I + 6, + y: nt + R / 2, + dominantBaseline: 'middle', + fontSize: '12', + fill: 'var(--color-fg-muted)', + 'aria-hidden': 'true', + children: Jh(j.duration), + }), + m.jsx('text', { + x: -10, + y: nt + R / 2, + textAnchor: 'end', + dominantBaseline: 'middle', + fontSize: '12', + fill: 'var(--color-fg-muted)', + 'aria-hidden': 'true', + children: j.label, + }), + ], + }, + Y, + ); + }), + m.jsx('line', { + x1: 0, + y1: 0, + x2: 0, + y2: V, + stroke: 'var(--color-fg-muted)', + strokeWidth: '1', + 'aria-hidden': 'true', + }), + m.jsx('line', { + x1: 0, + y1: V, + x2: h, + y2: V, + stroke: 'var(--color-fg-muted)', + strokeWidth: '1', + 'aria-hidden': 'true', + }), + ], + }), + }); + }; + function vv({ report: i, tests: c }) { + return m.jsxs(m.Fragment, { + children: [m.jsx(Ev, { report: i }), m.jsx(yv, { report: i, tests: c })], + }); + } + function yv({ report: i, tests: c }) { + const [u, f] = ue.useState(50); + return m.jsx(Vh, { + file: { + fileId: 'slowest', + fileName: 'Slowest Tests', + tests: c.slice(0, u), + stats: null, + }, + projectNames: i.json().projectNames, + footer: + u < c.length + ? m.jsxs('button', { + className: 'link-badge fullwidth-link', + style: { padding: '8px 5px' }, + onClick: () => f((r) => r + 50), + children: [Mi(), 'Show 50 more'], + }) + : void 0, + }); + } + function Ev({ report: i }) { + const c = i.json().machines; + if (c.length === 0) return null; + const u = c + .map((f) => { + const r = f.tag.join(' '), + o = new Date(f.startTime).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZoneName: 'short', + }); + let h = `${r} started at ${o}, runs ${Jh(f.duration)}`; + return ( + f.shardIndex && (h += ` (shard ${f.shardIndex})`), + { + label: r, + tooltip: h, + startTime: f.startTime, + duration: f.duration, + shardIndex: f.shardIndex ?? 1, + } + ); + }) + .sort((f, r) => f.label.localeCompare(r.label) || f.shardIndex - r.shardIndex); + return m.jsx(ke, { header: 'Timeline', children: m.jsx(Av, { entries: u }) }); + } + const pv = (i) => !i.has('testId') && !i.has('speedboard'), + xv = (i) => i.has('testId'), + bv = (i) => i.has('speedboard') && !i.has('testId'), + Sv = ({ report: i }) => { + var H, j; + const c = se(), + [u, f] = it.useState(new Map()), + [r, o] = it.useState(c.get('q') || ''), + [h, y] = it.useState(!1), + [v, A] = it.useState(!0), + x = c.has('speedboard'), + [T] = Hh('mergeFiles', !1), + D = c.get('testId'), + X = ((H = c.get('q')) == null ? void 0 : H.toString()) || '', + q = X ? '&q=' + X : '', + p = (j = i == null ? void 0 : i.json()) == null ? void 0 : j.options.title, + E = it.useMemo(() => { + const Y = new Map(); + for (const z of (i == null ? void 0 : i.json().files) || []) + for (const I of z.tests) Y.set(I.testId, z.fileId); + return Y; + }, [i]), + b = it.useMemo(() => uc.parse(r), [r]), + R = it.useMemo( + () => + b.empty() + ? void 0 + : Cv((i == null ? void 0 : i.json().files) || [], b), + [i, b], + ), + N = it.useMemo( + () => (x ? Rv(i, b) : T ? Dv(i, b) : Ov(i, b)), + [i, b, T, x], + ), + { prev: V, next: F } = it.useMemo(() => { + const Y = N.tests.findIndex((k) => k.testId === D), + z = Y > 0 ? N.tests[Y - 1] : void 0, + I = Y < N.tests.length - 1 ? N.tests[Y + 1] : void 0; + return { prev: z, next: I }; + }, [D, N]); + return ( + it.useEffect(() => { + const Y = (z) => { + if ( + z.target instanceof HTMLInputElement || + z.target instanceof HTMLTextAreaElement || + z.shiftKey || + z.ctrlKey || + z.metaKey || + z.altKey + ) + return; + const I = new URLSearchParams(c); + switch (z.key) { + case 'a': + (z.preventDefault(), ll('#?')); + break; + case 'p': + (z.preventDefault(), + I.delete('testId'), + I.delete('speedboard'), + ll(Ml(I, 's:passed', !1))); + break; + case 'f': + (z.preventDefault(), + I.delete('testId'), + I.delete('speedboard'), + ll(Ml(I, 's:failed', !1))); + break; + case 'ArrowLeft': + V && + (z.preventDefault(), + I.delete('testId'), + ll(il({ test: V }, I) + q)); + break; + case 'ArrowRight': + F && + (z.preventDefault(), + I.delete('testId'), + ll(il({ test: F }, I) + q)); + break; + } + }; + return ( + document.addEventListener('keydown', Y), + () => document.removeEventListener('keydown', Y) + ); + }, [V, F, q, X, c]), + it.useEffect(() => { + p ? (document.title = p) : (document.title = 'Playwright Test Report'); + }, [p]), + m.jsx('div', { + className: 'htmlreport vbox px-4 pb-4', + children: m.jsxs('main', { + children: [ + i && + m.jsx(z5, { + stats: i.json().stats, + filterText: r, + setFilterText: o, + }), + m.jsxs(Vf, { + predicate: pv, + children: [ + m.jsx(D2, { + report: i == null ? void 0 : i.json(), + filteredStats: R, + metadataVisible: h, + toggleMetadataVisible: () => y((Y) => !Y), + errorsVisible: v, + setErrorsVisible: A, + }), + m.jsx(gv, { + files: N.files, + expandedFiles: u, + setExpandedFiles: f, + projectNames: + (i == null ? void 0 : i.json().projectNames) || + [], + }), + ], + }), + m.jsxs(Vf, { + predicate: bv, + children: [ + m.jsx(D2, { + report: i == null ? void 0 : i.json(), + filteredStats: R, + metadataVisible: h, + toggleMetadataVisible: () => y((Y) => !Y), + errorsVisible: v, + setErrorsVisible: A, + }), + i && m.jsx(vv, { report: i, tests: N.tests }), + ], + }), + m.jsx(Vf, { + predicate: xv, + children: + i && + m.jsx(Tv, { + report: i, + next: F, + prev: V, + testId: D, + testIdToFileIdMap: E, + }), + }), + ], + }), + }) + ); + }, + Tv = ({ report: i, testIdToFileIdMap: c, next: u, prev: f, testId: r }) => { + const [o, h] = it.useState('loading'), + y = +(se().get('run') || '0'); + return ( + it.useEffect(() => { + (async () => { + if (!r || (typeof o == 'object' && r === o.testId)) return; + const v = c.get(r); + if (!v) { + h('not-found'); + return; + } + const A = await i.entry(`${v}.json`); + h( + (A == null ? void 0 : A.tests.find((x) => x.testId === r)) || + 'not-found', + ); + })(); + }, [o, i, r, c]), + o === 'loading' + ? m.jsx('div', { className: 'test-case-column' }) + : o === 'not-found' + ? m.jsxs('div', { + className: 'test-case-column', + children: [ + m.jsx(mr, { title: 'Test not found' }), + m.jsxs('div', { + className: 'test-case-location', + children: ['Test ID: ', r], + }), + ], + }) + : m.jsx('div', { + className: 'test-case-column', + children: m.jsx(cv, { + report: i, + next: u, + prev: f, + test: o, + run: y, + }), + }) + ); + }; + function Cv(i, c) { + const u = { total: 0, duration: 0 }; + for (const f of i) { + const r = f.tests.filter((o) => c.matches(o)); + u.total += r.length; + for (const o of r) u.duration += o.duration; + } + return u; + } + function Ov(i, c) { + const u = { files: [], tests: [] }; + for (const f of (i == null ? void 0 : i.json().files) || []) { + const r = f.tests.filter((o) => c.matches(o)); + (r.length && u.files.push({ ...f, tests: r }), u.tests.push(...r)); + } + return u; + } + function Dv(i, c) { + const u = [], + f = new Map(); + for (const o of (i == null ? void 0 : i.json().files) || []) { + const h = o.tests.filter((y) => c.matches(y)); + for (const y of h) { + const v = y.path[0] ?? ''; + let A = f.get(v); + A || + ((A = { + fileId: v, + fileName: v, + tests: [], + stats: { + total: 0, + expected: 0, + unexpected: 0, + flaky: 0, + skipped: 0, + ok: !0, + }, + }), + f.set(v, A), + u.push(A)); + const x = { ...y, path: y.path.slice(1) }; + A.tests.push(x); + } + } + u.sort((o, h) => o.fileName.localeCompare(h.fileName)); + const r = { files: u, tests: [] }; + for (const o of u) r.tests.push(...o.tests); + return r; + } + function Rv(i, c) { + const f = ((i == null ? void 0 : i.json().files) || []) + .flatMap((r) => r.tests) + .filter((r) => c.matches(r)); + return (f.sort((r, o) => o.duration - r.duration), { files: [], tests: f }); + } + const wv = + "data:image/svg+xml,%3csvg%20width='400'%20height='400'%20viewBox='0%200%20400%20400'%20fill='none'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='M136.444%20221.556C123.558%20225.213%20115.104%20231.625%20109.535%20238.032C114.869%20233.364%20122.014%20229.08%20131.652%20226.348C141.51%20223.554%20149.92%20223.574%20156.869%20224.915V219.481C150.941%20218.939%20144.145%20219.371%20136.444%20221.556ZM108.946%20175.876L61.0895%20188.484C61.0895%20188.484%2061.9617%20189.716%2063.5767%20191.36L104.153%20180.668C104.153%20180.668%20103.578%20188.077%2098.5847%20194.705C108.03%20187.559%20108.946%20175.876%20108.946%20175.876ZM149.005%20288.347C81.6582%20306.486%2046.0272%20228.438%2035.2396%20187.928C30.2556%20169.229%2028.0799%20155.067%2027.5%20145.928C27.4377%20144.979%2027.4665%20144.179%2027.5336%20143.446C24.04%20143.657%2022.3674%20145.473%2022.7077%20150.721C23.2876%20159.855%2025.4633%20174.016%2030.4473%20192.721C41.2301%20233.225%2076.8659%20311.273%20144.213%20293.134C158.872%20289.185%20169.885%20281.992%20178.152%20272.81C170.532%20279.692%20160.995%20285.112%20149.005%20288.347ZM161.661%20128.11V132.903H188.077C187.535%20131.206%20186.989%20129.677%20186.447%20128.11H161.661Z'%20fill='%232D4552'/%3e%3cpath%20d='M193.981%20167.584C205.861%20170.958%20212.144%20179.287%20215.465%20186.658L228.711%20190.42C228.711%20190.42%20226.904%20164.623%20203.57%20157.995C181.741%20151.793%20168.308%20170.124%20166.674%20172.496C173.024%20167.972%20182.297%20164.268%20193.981%20167.584ZM299.422%20186.777C277.573%20180.547%20264.145%20198.916%20262.535%20201.255C268.89%20196.736%20278.158%20193.031%20289.837%20196.362C301.698%20199.741%20307.976%20208.06%20311.307%20215.436L324.572%20219.212C324.572%20219.212%20322.736%20193.41%20299.422%20186.777ZM286.262%20254.795L176.072%20223.99C176.072%20223.99%20177.265%20230.038%20181.842%20237.869L274.617%20263.805C282.255%20259.386%20286.262%20254.795%20286.262%20254.795ZM209.867%20321.102C122.618%20297.71%20133.166%20186.543%20147.284%20133.865C153.097%20112.156%20159.073%2096.0203%20164.029%2085.204C161.072%2084.5953%20158.623%2086.1529%20156.203%2091.0746C150.941%20101.747%20144.212%20119.124%20137.7%20143.45C123.586%20196.127%20113.038%20307.29%20200.283%20330.682C241.406%20341.699%20273.442%20324.955%20297.323%20298.659C274.655%20319.19%20245.714%20330.701%20209.867%20321.102Z'%20fill='%232D4552'/%3e%3cpath%20d='M161.661%20262.296V239.863L99.3324%20257.537C99.3324%20257.537%20103.938%20230.777%20136.444%20221.556C146.302%20218.762%20154.713%20218.781%20161.661%20220.123V128.11H192.869C189.471%20117.61%20186.184%20109.526%20183.423%20103.909C178.856%2094.612%20174.174%20100.775%20163.545%20109.665C156.059%20115.919%20137.139%20129.261%20108.668%20136.933C80.1966%20144.61%2057.179%20142.574%2047.5752%20140.911C33.9601%20138.562%2026.8387%20135.572%2027.5049%20145.928C28.0847%20155.062%2030.2605%20169.224%2035.2445%20187.928C46.0272%20228.433%2081.663%20306.481%20149.01%20288.342C166.602%20283.602%20179.019%20274.233%20187.626%20262.291H161.661V262.296ZM61.0848%20188.484L108.946%20175.876C108.946%20175.876%20107.551%20194.288%2089.6087%20199.018C71.6614%20203.743%2061.0848%20188.484%2061.0848%20188.484Z'%20fill='%23E2574C'/%3e%3cpath%20d='M341.786%20129.174C329.345%20131.355%20299.498%20134.072%20262.612%20124.185C225.716%20114.304%20201.236%2097.0224%20191.537%2088.8994C177.788%2077.3834%20171.74%2069.3802%20165.788%2081.4857C160.526%2092.163%20153.797%20109.54%20147.284%20133.866C133.171%20186.543%20122.623%20297.706%20209.867%20321.098C297.093%20344.47%20343.53%20242.92%20357.644%20190.238C364.157%20165.917%20367.013%20147.5%20367.799%20135.625C368.695%20122.173%20359.455%20126.078%20341.786%20129.174ZM166.497%20172.756C166.497%20172.756%20180.246%20151.372%20203.565%20158C226.899%20164.628%20228.706%20190.425%20228.706%20190.425L166.497%20172.756ZM223.42%20268.713C182.403%20256.698%20176.077%20223.99%20176.077%20223.99L286.262%20254.796C286.262%20254.791%20264.021%20280.578%20223.42%20268.713ZM262.377%20201.495C262.377%20201.495%20276.107%20180.126%20299.422%20186.773C322.736%20193.411%20324.572%20219.208%20324.572%20219.208L262.377%20201.495Z'%20fill='%232EAD33'/%3e%3cpath%20d='M139.88%20246.04L99.3324%20257.532C99.3324%20257.532%20103.737%20232.44%20133.607%20222.496L110.647%20136.33L108.663%20136.933C80.1918%20144.611%2057.1742%20142.574%2047.5704%20140.911C33.9554%20138.563%2026.834%20135.572%2027.5001%20145.929C28.08%20155.063%2030.2557%20169.224%2035.2397%20187.929C46.0225%20228.433%2081.6583%20306.481%20149.005%20288.342L150.989%20287.719L139.88%20246.04ZM61.0848%20188.485L108.946%20175.876C108.946%20175.876%20107.551%20194.288%2089.6087%20199.018C71.6615%20203.743%2061.0848%20188.485%2061.0848%20188.485Z'%20fill='%23D65348'/%3e%3cpath%20d='M225.27%20269.163L223.415%20268.712C182.398%20256.698%20176.072%20223.99%20176.072%20223.99L232.89%20239.872L262.971%20124.281L262.607%20124.185C225.711%20114.304%20201.232%2097.0224%20191.532%2088.8994C177.783%2077.3834%20171.735%2069.3802%20165.783%2081.4857C160.526%2092.163%20153.797%20109.54%20147.284%20133.866C133.171%20186.543%20122.623%20297.706%20209.867%20321.097L211.655%20321.5L225.27%20269.163ZM166.497%20172.756C166.497%20172.756%20180.246%20151.372%20203.565%20158C226.899%20164.628%20228.706%20190.425%20228.706%20190.425L166.497%20172.756Z'%20fill='%231D8D22'/%3e%3cpath%20d='M141.946%20245.451L131.072%20248.537C133.641%20263.019%20138.169%20276.917%20145.276%20289.195C146.513%20288.922%20147.74%20288.687%20149%20288.342C152.302%20287.451%20155.364%20286.348%20158.312%20285.145C150.371%20273.361%20145.118%20259.789%20141.946%20245.451ZM137.7%20143.451C132.112%20164.307%20127.113%20194.326%20128.489%20224.436C130.952%20223.367%20133.554%20222.371%20136.444%20221.551L138.457%20221.101C136.003%20188.939%20141.308%20156.165%20147.284%20133.866C148.799%20128.225%20150.318%20122.978%20151.832%20118.085C149.393%20119.637%20146.767%20121.228%20143.776%20122.867C141.759%20129.093%20139.722%20135.898%20137.7%20143.451Z'%20fill='%23C04B41'/%3e%3c/svg%3e", + If = P3, + vr = document.createElement('link'); + vr.rel = 'shortcut icon'; + vr.href = wv; + document.head.appendChild(vr); + const Mv = () => { + const [i, c] = it.useState(); + return ( + it.useEffect(() => { + const u = new jv(); + u.load().then(() => { + var f; + ((f = document.getElementById('playwrightReportBase64')) == null || + f.remove(), + c(u)); + }); + }, []), + m.jsx(C5, { children: m.jsx(Sv, { report: i }) }) + ); + }; + window.onload = () => { + (H5(), u5.createRoot(document.querySelector('#root')).render(m.jsx(Mv, {}))); + }; + class jv { + constructor() { + this._entries = new Map(); + } + async load() { + const c = document.getElementById('playwrightReportBase64').content.textContent, + u = new If.ZipReader(new If.Data64URIReader(c), { useWebWorkers: !1 }); + for (const f of await u.getEntries()) this._entries.set(f.filename, f); + this._json = await this.entry('report.json'); + } + json() { + return this._json; + } + async entry(c) { + const u = this._entries.get(c), + f = new If.TextWriter(); + return (await u.getData(f), JSON.parse(await f.getData())); + } + } + + + + +
+ - \ No newline at end of file + diff --git a/playwright.config.js b/playwright.config.js index c5dc324..c1792d1 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -1,25 +1,25 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ - testDir: './tests', - fullyParallel: true, - forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, - reporter: 'html', - use: { - baseURL: 'http://localhost:5173', - trace: 'on-first-retry', - }, - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://localhost:5173', + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, }, - ], - webServer: { - command: 'npm run dev', - url: 'http://localhost:5173', - reuseExistingServer: !process.env.CI, - }, }); diff --git a/src/counter.js b/src/counter.js index 12bf115..3621951 100644 --- a/src/counter.js +++ b/src/counter.js @@ -1,9 +1,9 @@ export function setupCounter(element) { - let counter = 0 - const setCounter = (count) => { - counter = count - element.innerHTML = `Count is ${counter}` - } - element.addEventListener('click', () => setCounter(counter + 1)) - setCounter(0) + let counter = 0; + const setCounter = (count) => { + counter = count; + element.innerHTML = `Count is ${counter}`; + }; + element.addEventListener('click', () => setCounter(counter + 1)); + setCounter(0); } diff --git a/src/main.js b/src/main.js index c53b547..c53937d 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,8 @@ -import './style.css' -import javascriptLogo from './assets/javascript.svg' -import viteLogo from './assets/vite.svg' -import heroImg from './assets/hero.png' -import { setupCounter } from './counter.js' +import './style.css'; +import javascriptLogo from './assets/javascript.svg'; +import viteLogo from './assets/vite.svg'; +import heroImg from './assets/hero.png'; +import { setupCounter } from './counter.js'; document.querySelector('#app').innerHTML = `
@@ -55,6 +55,6 @@ document.querySelector('#app').innerHTML = `
-` +`; -setupCounter(document.querySelector('#counter')) +setupCounter(document.querySelector('#counter')); diff --git a/src/style.css b/src/style.css index 527d4fb..404be75 100644 --- a/src/style.css +++ b/src/style.css @@ -1,296 +1,292 @@ :root { - --text: #6b6375; - --text-h: #08060d; - --bg: #fff; - --border: #e5e4e7; - --code-bg: #f4f3ec; - --accent: #aa3bff; - --accent-bg: rgba(170, 59, 255, 0.1); - --accent-border: rgba(170, 59, 255, 0.5); - --social-bg: rgba(244, 243, 236, 0.5); - --shadow: - rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; - - --sans: system-ui, 'Segoe UI', Roboto, sans-serif; - --heading: system-ui, 'Segoe UI', Roboto, sans-serif; - --mono: ui-monospace, Consolas, monospace; - - font: 18px/145% var(--sans); - letter-spacing: 0.18px; - color-scheme: light dark; - color: var(--text); - background: var(--bg); - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - @media (max-width: 1024px) { - font-size: 16px; - } + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } } @media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: - rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } - - #social .button-icon { - filter: invert(1) brightness(2); - } + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } } body { - margin: 0; + margin: 0; } h1, h2 { - font-family: var(--heading); - font-weight: 500; - color: var(--text-h); + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); } h1 { - font-size: 56px; - letter-spacing: -1.68px; - margin: 32px 0; - @media (max-width: 1024px) { - font-size: 36px; - margin: 20px 0; - } + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } } h2 { - font-size: 24px; - line-height: 118%; - letter-spacing: -0.24px; - margin: 0 0 8px; - @media (max-width: 1024px) { - font-size: 20px; - } + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } } p { - margin: 0; + margin: 0; } code, .counter { - font-family: var(--mono); - display: inline-flex; - border-radius: 4px; - color: var(--text-h); + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); } code { - font-size: 15px; - line-height: 135%; - padding: 4px 8px; - background: var(--code-bg); + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); } .counter { - font-size: 16px; - padding: 5px 10px; - border-radius: 5px; - color: var(--accent); - background: var(--accent-bg); - border: 2px solid transparent; - transition: border-color 0.3s; - margin-bottom: 24px; - - &:hover { - border-color: var(--accent-border); - } - &:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } } .hero { - position: relative; + position: relative; - .base, - .framework, - .vite { - inset-inline: 0; - margin: 0 auto; - } + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } - .base { - width: 170px; - position: relative; - z-index: 0; - } - - .framework, - .vite { - position: absolute; - } - - .framework { - z-index: 1; - top: 34px; - height: 28px; - transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) - scale(1.4); - } - - .vite { - z-index: 0; - top: 107px; - height: 26px; - width: auto; - transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) - scale(0.8); - } + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) scale(0.8); + } } #app { - width: 1126px; - max-width: 100%; - margin: 0 auto; - text-align: center; - border-inline: 1px solid var(--border); - min-height: 100svh; - display: flex; - flex-direction: column; - box-sizing: border-box; + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; } #center { - display: flex; - flex-direction: column; - gap: 25px; - place-content: center; - place-items: center; - flex-grow: 1; - - @media (max-width: 1024px) { - padding: 32px 20px 24px; - gap: 18px; - } + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } } #next-steps { - display: flex; - border-top: 1px solid var(--border); - text-align: left; - - & > div { - flex: 1 1 0; - padding: 32px; - @media (max-width: 1024px) { - padding: 24px 20px; + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } } - } - .icon { - margin-bottom: 16px; - width: 22px; - height: 22px; - } + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } - @media (max-width: 1024px) { - flex-direction: column; - text-align: center; - } + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } } #docs { - border-right: 1px solid var(--border); + border-right: 1px solid var(--border); - @media (max-width: 1024px) { - border-right: none; - border-bottom: 1px solid var(--border); - } + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } } #next-steps ul { - list-style: none; - padding: 0; - display: flex; - gap: 8px; - margin: 32px 0 0; - - .logo { - height: 18px; - } - - a { - color: var(--text-h); - font-size: 16px; - border-radius: 6px; - background: var(--social-bg); + list-style: none; + padding: 0; display: flex; - padding: 6px 12px; - align-items: center; gap: 8px; - text-decoration: none; - transition: box-shadow 0.3s; + margin: 32px 0 0; - &:hover { - box-shadow: var(--shadow); + .logo { + height: 18px; } - .button-icon { - height: 18px; - width: 18px; - } - } - - @media (max-width: 1024px) { - margin-top: 20px; - flex-wrap: wrap; - justify-content: center; - li { - flex: 1 1 calc(50% - 8px); + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } } - a { - width: 100%; - justify-content: center; - box-sizing: border-box; + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } } - } } #spacer { - height: 88px; - border-top: 1px solid var(--border); - @media (max-width: 1024px) { - height: 48px; - } + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } } .ticks { - position: relative; - width: 100%; - - &::before, - &::after { - content: ''; - position: absolute; - top: -4.5px; - border: 5px solid transparent; - } - - &::before { - left: 0; - border-left-color: var(--border); - } - &::after { - right: 0; - border-right-color: var(--border); - } + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } } diff --git a/test-results/.last-run.json b/test-results/.last-run.json index cbcc1fb..c2cf3c4 100644 --- a/test-results/.last-run.json +++ b/test-results/.last-run.json @@ -1,4 +1,4 @@ { - "status": "passed", - "failedTests": [] -} \ No newline at end of file + "status": "passed", + "failedTests": [] +} diff --git a/tests/accessibility.spec.js b/tests/accessibility.spec.js index d54d04a..40d66ca 100644 --- a/tests/accessibility.spec.js +++ b/tests/accessibility.spec.js @@ -2,29 +2,29 @@ import { test, expect } from '@playwright/test'; import AxeBuilder from '@axe-core/playwright'; test.describe('Accessibility Audits', () => { - test('Check Main Page for WCAG compliance', async ({ page }) => { - await page.goto('/'); + test('Check Main Page for WCAG compliance', async ({ page }) => { + await page.goto('/'); - // Wait for animations to settle - await page.waitForTimeout(1000); + // Wait for animations to settle + await page.waitForTimeout(1000); - const accessibilityScanResults = await new AxeBuilder({ page }) - .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) - .analyze(); + const accessibilityScanResults = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) + .analyze(); - expect(accessibilityScanResults.violations).toEqual([]); - }); + expect(accessibilityScanResults.violations).toEqual([]); + }); - test('Check Landmarks and Semantic Roles', async ({ page }) => { - await page.goto('/'); + test('Check Landmarks and Semantic Roles', async ({ page }) => { + await page.goto('/'); - const main = page.locator('main#content'); - await expect(main).toBeVisible(); + const main = page.locator('main#content'); + await expect(main).toBeVisible(); - const header = page.locator('header.hero'); - await expect(header).toBeVisible(); + const header = page.locator('header.hero'); + await expect(header).toBeVisible(); - const footer = page.locator('footer'); - await expect(footer).toBeVisible(); - }); + const footer = page.locator('footer'); + await expect(footer).toBeVisible(); + }); }); diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js index cc5122d..ec40341 100644 --- a/tests/smoke.spec.js +++ b/tests/smoke.spec.js @@ -1,45 +1,45 @@ import { test, expect } from '@playwright/test'; test.describe('Portfolio Smoke Tests', () => { - test.beforeEach(async ({ page }) => { - await page.goto('/'); - }); - - test('Check Page Title', async ({ page }) => { - await expect(page).toHaveTitle(/Simon Foster/); - }); - - test('Check Hero Section Content', async ({ page }) => { - const heroTitle = page.locator('h1'); - await expect(heroTitle).toContainText('Simon Foster'); - - const heroDescription = page.locator('.hero p').first(); - await expect(heroDescription).toContainText('Technical Leader'); - }); - - test('Check Main Sections Existence', async ({ page }) => { - const sections = ['#about', '#focus', '#experience', '#education', '#interests']; - for (const section of sections) { - const el = page.locator(section); - await expect(el).toBeVisible(); - } - }); - - test('Verify External Social Links', async ({ page }) => { - const linkedIn = page.locator('a[aria-label="LinkedIn"]'); - await expect(linkedIn).toHaveAttribute('href', /linkedin.com/); - await expect(linkedIn).toHaveAttribute('rel', 'noopener noreferrer'); - - const github = page.locator('a[aria-label="GitHub"]'); - await expect(github).toHaveAttribute('href', /github.com/); - await expect(github).toHaveAttribute('rel', 'noopener noreferrer'); - }); - - test('Navigation scrolls to sections', async ({ page }) => { - const experienceLink = page.locator('nav a[href="#experience"]'); - await experienceLink.click(); - - // Check if the experience section is in view or the URL has hash - await expect(page).toHaveURL(/#experience/); - }); + test.beforeEach(async ({ page }) => { + await page.goto('/'); + }); + + test('Check Page Title', async ({ page }) => { + await expect(page).toHaveTitle(/Simon Foster/); + }); + + test('Check Hero Section Content', async ({ page }) => { + const heroTitle = page.locator('h1'); + await expect(heroTitle).toContainText('Simon Foster'); + + const heroDescription = page.locator('.hero p').first(); + await expect(heroDescription).toContainText('Technical Leader'); + }); + + test('Check Main Sections Existence', async ({ page }) => { + const sections = ['#about', '#focus', '#experience', '#education', '#interests']; + for (const section of sections) { + const el = page.locator(section); + await expect(el).toBeVisible(); + } + }); + + test('Verify External Social Links', async ({ page }) => { + const linkedIn = page.locator('a[aria-label="LinkedIn"]'); + await expect(linkedIn).toHaveAttribute('href', /linkedin.com/); + await expect(linkedIn).toHaveAttribute('rel', 'noopener noreferrer'); + + const github = page.locator('a[aria-label="GitHub"]'); + await expect(github).toHaveAttribute('href', /github.com/); + await expect(github).toHaveAttribute('rel', 'noopener noreferrer'); + }); + + test('Navigation scrolls to sections', async ({ page }) => { + const experienceLink = page.locator('nav a[href="#experience"]'); + await experienceLink.click(); + + // Check if the experience section is in view or the URL has hash + await expect(page).toHaveURL(/#experience/); + }); }); diff --git a/vite.config.js b/vite.config.js index a39137c..6be3fd8 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,8 +2,6 @@ import { defineConfig } from 'vite'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ - base: '', - plugins: [ - tailwindcss(), - ], + base: '', + plugins: [tailwindcss()], }); From ebd9dba381a23dd710b8aba5b746b0be9fc85165 Mon Sep 17 00:00:00 2001 From: Simon Foster Date: Thu, 9 Apr 2026 21:59:20 +0100 Subject: [PATCH 3/6] [chore] update gitignore file --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index a547bf3..cab3a32 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ dist-ssr *.njsproj *.sln *.sw? + +# Playwright +playwright-report/ +test-results/ +playwright/.cache/ From ba1607677c8fd28808c2677796f54dfefeae8ef6 Mon Sep 17 00:00:00 2001 From: Simon Foster Date: Thu, 9 Apr 2026 22:02:56 +0100 Subject: [PATCH 4/6] [chore] remove files now in gitignore --- playwright-report/index.html | 23255 --------------------------------- test-results/.last-run.json | 4 - 2 files changed, 23259 deletions(-) delete mode 100644 playwright-report/index.html delete mode 100644 test-results/.last-run.json diff --git a/playwright-report/index.html b/playwright-report/index.html deleted file mode 100644 index 44f20bf..0000000 --- a/playwright-report/index.html +++ /dev/null @@ -1,23255 +0,0 @@ - - - - - - - Playwright Test Report - - - - -
- - - diff --git a/test-results/.last-run.json b/test-results/.last-run.json deleted file mode 100644 index c2cf3c4..0000000 --- a/test-results/.last-run.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "status": "passed", - "failedTests": [] -} From 0525ea04d8b4d37fac3e370a65687099115f11b2 Mon Sep 17 00:00:00 2001 From: Simon Foster Date: Thu, 9 Apr 2026 22:11:07 +0100 Subject: [PATCH 5/6] [feat] add a favicon --- index.html | 2 ++ main.js | 1 - public/favicon.svg | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 7e4eede..e3026ab 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,12 @@ Simon Foster | Full-Stack Engineer & Technical Leader + + diff --git a/main.js b/main.js index ffe724a..482865e 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,3 @@ -import './index.css'; // Navbar Scroll Effect const navbar = document.getElementById('navbar'); diff --git a/public/favicon.svg b/public/favicon.svg index 0906f9c..a34b6e9 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1 +1,4 @@ - + + + SF. + From 93918af4e02f070d6f3a0c4f41c8ee2068bbfa4a Mon Sep 17 00:00:00 2001 From: Simon Foster Date: Thu, 9 Apr 2026 22:28:07 +0100 Subject: [PATCH 6/6] [feat] update hover effects, timing and glows --- index.css | 9 +- index.html | 845 +++++++++++++++++++++++++---------------------------- 2 files changed, 400 insertions(+), 454 deletions(-) diff --git a/index.css b/index.css index a401db3..8cf3063 100644 --- a/index.css +++ b/index.css @@ -113,10 +113,10 @@ .glass-card:hover { @apply z-10 -translate-y-2 scale-[1.02] bg-[rgba(255,255,255,0.04)]; transition-duration: 300ms; - border-color: rgba(0, 212, 255, 0.2); + border-color: rgba(0, 212, 255, 0.3); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6), - 0 0 20px rgba(0, 212, 255, 0.05); + 0 0 40px rgba(0, 212, 255, 0.15); } .glass-card:hover .tech-tag { @@ -157,13 +157,14 @@ } .emoji { - @apply inline-block shrink-0 text-[1.5em] transition-all duration-300; + @apply inline-block shrink-0 text-[1.5em] transition-[var(--transition-slow)]; line-height: 1.1; filter: drop-shadow(0 0 12px rgba(0, 212, 255, 0.3)); } .glass-card:hover .emoji { - @apply -translate-y-1 scale-125; + @apply scale-125 -translate-y-1; + transition-duration: 300ms; filter: drop-shadow(0 5px 25px rgba(0, 212, 255, 0.7)); } diff --git a/index.html b/index.html index e3026ab..cd6debd 100644 --- a/index.html +++ b/index.html @@ -1,501 +1,446 @@ - - - - Simon Foster | Full-Stack Engineer & Technical Leader - - - - - -
-
-
+ + + + Simon Foster | Full-Stack Engineer & Technical Leader + + + + - + +
+
+
- -
-
-
-
- Experienced Full-Stack Engineer - - Technical Leader - - 30 Years of Digital Transformation -
-

- Simon Foster -

-
- AWS TypeScript - PHP - - React -
-

- Technical Leader with a long track record of turning messy, outdated, or - complex systems into clean, scalable platforms. A hands-on engineer focused - on solving real problems, guiding teams, and delivering solutions that move - businesses forward. +

+
+ Simon Foster +
+
+
+ +
+ +
+
+

Strategic Technical Focus

+
+
+

+ Engineering Philosophy +

+

+ I enjoy building things that last, supporting the people who build them, + and making technology feel like an enabler rather than a blocker.

-

- I’ve led development teams, modernised legacy stacks, and built scalable - platforms across PHP, JavaScript, and cloud-native environments. I thrive - where technical depth meets practical decision-making and continuous - improvement. +

+
+

+ Event-Driven + Architecture +

+

+ Expert in building scalable microservices and decoupled architectures + using AWS (Lambda, SQS, SNS, DynamoDB), Kubernetes, and Docker.

-
- -
-
- Simon Foster -
+
+

+ Full-Stack Ecosystems +

+

+ Architecting type-safe backends in Node.js (TypeScript) and PHP + (Symfony/Laravel), paired with React/Next.js frontends. +

+
+
+

+ Engineering Integrity +

+

+ Strict advocate for SOLID, Clean Architecture, and CI/CD; transitioning + to strongly-typed systems to reduce technical debt. +

+
+
+

Design-to-Code

+

+ Bridging the gap between creative vision and execution leveraging tools + like Figma for high-fidelity implementation. +

- - +
-
-
-

Strategic Technical Focus

-
-
-

- Engineering Philosophy -

-

- I enjoy building things that last, supporting the people who build them, - and making technology feel like an enabler rather than a blocker. -

-
-
-

- Event-Driven - Architecture -

+
+

Experience

+
+ +
+
+
+

Lead Developer

+ Nov 2022 - Present +
+
Travel Chapter

- Expert in building scalable microservices and decoupled architectures - using AWS (Lambda, SQS, SNS, DynamoDB), Kubernetes, and Docker. + As a hands-on Lead Developer, my main focus is the technical + integration and continuation of two technologically different + business stacks, architecting solutions that ensure seamless data + continuity and zero service disruption. I collaborate closely with + wider business stakeholders and technical teams to translate complex + requirements into scalable roadmaps, focusing on modernising legacy + structures within cloud-native environments using AWS.

-
-
-

- Full-Stack Ecosystems -

- Architecting type-safe backends in Node.js (TypeScript) and PHP - (Symfony/Laravel), paired with React/Next.js frontends. + I manage and mentor a cross-functional development team, fostering a + culture of engineering excellence and continuous improvement. By + championing clean coding principles and establishing robust CI/CD + practices via GitLab, SonarQube and AWS CDK, I ensure the consistent + delivery of high-quality, maintainable code across our PHP (Symfony) + and TypeScript (Node/React) ecosystems.

+
+ PHP (Symfony) + SQL (MySQL) + TypeScript (Node/React) + AWS (CDK) + Kubernetes + GitLab (CI/CD) + SonarQube + Jira +
-
-

- Engineering Integrity -

+
+ + +
+
+
+

Lead Developer

+ Sept 2021 - Oct 2022 +
+
The Original Cottage Company

- Strict advocate for SOLID, Clean Architecture, and CI/CD; transitioning - to strongly-typed systems to reduce technical debt. + In this leadership role, I directed a team of developers in close + partnership with product owners and business analysts to deliver + high-impact solutions across customer focused products. Translating + stakeholder requirements into technical reality, ensuring that + innovative features were delivered in a timely manner without + compromising on architectural integrity.

-
-
-

Design-to-Code

- Bridging the gap between creative vision and execution leveraging tools - like Figma for high-fidelity implementation. + I acted as a mentor and collaborator, championing the delivery of + consistent, clean code. By leveraging tools like Bitbucket Pipelines + and Jenkins within an AWS Serverless environment, I streamlined + development workflows to ensure the team met both technical + standards and evolving business needs with maximum efficiency.

+
+ PHP (Symfony) + SQL (MySQL) + JavaScript (React) + AWS Serverless + Bitbucket Pipelines + Jenkins + Jira +
-
-
-

Experience

-
- -
-
-
-

Lead Developer

- Nov 2022 - Present -
-
Travel Chapter
-

- As a hands-on Lead Developer, my main focus is the technical - integration and continuation of two technologically different - business stacks, architecting solutions that ensure seamless data - continuity and zero service disruption. I collaborate closely with - wider business stakeholders and technical teams to translate complex - requirements into scalable roadmaps, focusing on modernising legacy - structures within cloud-native environments using AWS. -

-

- I manage and mentor a cross-functional development team, fostering a - culture of engineering excellence and continuous improvement. By - championing clean coding principles and establishing robust CI/CD - practices via GitLab, SonarQube and AWS CDK, I ensure the consistent - delivery of high-quality, maintainable code across our PHP (Symfony) - and TypeScript (Node/React) ecosystems. -

-
- PHP (Symfony) - SQL (MySQL) - TypeScript (Node/React) - AWS (CDK) - Kubernetes - GitLab (CI/CD) - SonarQube - Jira -
+ +
+
+
+

Developer

+ May 2019 - Sept 2021
-
- - -
-
-
-

Lead Developer

- Sept 2021 - Oct 2022 -
-
The Original Cottage Company
-

- In this leadership role, I directed a team of developers in close - partnership with product owners and business analysts to deliver - high-impact solutions across customer focused products. Translating - stakeholder requirements into technical reality, ensuring that - innovative features were delivered in a timely manner without - compromising on architectural integrity. -

-

- I acted as a mentor and collaborator, championing the delivery of - consistent, clean code. By leveraging tools like Bitbucket Pipelines - and Jenkins within an AWS Serverless environment, I streamlined - development workflows to ensure the team met both technical - standards and evolving business needs with maximum efficiency. -

-
- PHP (Symfony) - SQL (MySQL) - JavaScript (React) - AWS Serverless - Bitbucket Pipelines - Jenkins - Jira -
+
The Original Cottage Company
+

+ My primary focus was the continuous improvement of the group’s + digital presence, where I developed new features aimed at optimising + the customer journey and driving user engagement. I performed + critical maintenance across the web estate and worked extensively + with the core booking system API to ensure a high-performance, + seamless experience for guests and owners. +

+

+ Working within a modern PHP and JavaScript stack, I prioritised + efficiency and speed, ensuring the integration between the frontend + platforms and backend services was robust and scalable. This role + was fundamental in refining my expertise in AWS Serverless + architecture and Drupal-based environments. +

+
+ PHP (Symfony) + SQL (MySQL) + JavaScript (React) + AWS Serverless + Drupal + Jenkins + Bitbucket
+
- -
-
-
-

Developer

- May 2019 - Sept 2021 -
-
The Original Cottage Company
-

- My primary focus was the continuous improvement of the group’s - digital presence, where I developed new features aimed at optimising - the customer journey and driving user engagement. I performed - critical maintenance across the web estate and worked extensively - with the core booking system API to ensure a high-performance, - seamless experience for guests and owners. -

-

- Working within a modern PHP and JavaScript stack, I prioritised - efficiency and speed, ensuring the integration between the frontend - platforms and backend services was robust and scalable. This role - was fundamental in refining my expertise in AWS Serverless - architecture and Drupal-based environments. -

-
- PHP (Symfony) - SQL (MySQL) - JavaScript (React) - AWS Serverless - Drupal - Jenkins - Bitbucket -
+ +
+
+
+

Technical Director / Senior Developer

+ Aug 1995 - May 2019
-
+
+ Cambridge Occupational Analysts (COA Ltd) +
+

+ As Technical Director, I had accountability for the IT department + and was involved with product strategy, bridging the gap between + directorial vision and technical execution. My primary focus was + spearheading the digital transformation of the company’s core + product, successfully migrating a legacy SQL Server and Access-based + ecosystem into a modern, high-performance web application driven by + RESTful APIs, Laravel, and Angular. +

+

+ I oversaw the maintenance of all existing products and + infrastructure while providing technical mentorship and leadership + to the development team. By modernising our internal workflows and + infrastructure via VMWare and Linux environments, I ensured high + system availability and a scalable foundation for new product + development. +

- -
-
-
-

Technical Director / Senior Developer

- Aug 1995 - May 2019 -
-
- Cambridge Occupational Analysts (COA Ltd) +
+
+ Lead Developer / IT Manager (Jan 2003 - Aug 2010) +

+ Managed the IT team responsible for the end-to-end design + and support of back-office and customer-facing web + applications. Directed the provision of server and network + infrastructure across Linux and Windows environments. +

-

- As Technical Director, I had accountability for the IT department - and was involved with product strategy, bridging the gap between - directorial vision and technical execution. My primary focus was - spearheading the digital transformation of the company’s core - product, successfully migrating a legacy SQL Server and Access-based - ecosystem into a modern, high-performance web application driven by - RESTful APIs, Laravel, and Angular. -

-

- I oversaw the maintenance of all existing products and - infrastructure while providing technical mentorship and leadership - to the development team. By modernising our internal workflows and - infrastructure via VMWare and Linux environments, I ensured high - system availability and a scalable foundation for new product - development. -

- -
-
- Lead Developer / IT Manager (Jan 2003 - Aug 2010) -

- Managed the IT team responsible for the end-to-end design - and support of back-office and customer-facing web - applications. Directed the provision of server and network - infrastructure across Linux and Windows environments. -

-
-
- Lead Developer (Jan 1998 - Jan 2003) -

- Engineered a new integrated database system for careers - reporting, replacing legacy RISC-OS systems with a connected - modern Windows/Linux infrastructure. -

-
-
- Software Developer (Aug 1995 - Jan 1998) -

- Maintenance and development of internal report-generation - systems and desktop support. -

-
+
+ Lead Developer (Jan 1998 - Jan 2003) +

+ Engineered a new integrated database system for careers + reporting, replacing legacy RISC-OS systems with a connected + modern Windows/Linux infrastructure. +

- -
- PHP (Laravel) - Angular (5+) - SQL (SQL Server) - Linux (Apache/NGINX) - VMWare - VB6 / VBA - C/C++ - Windows Server +
+ Software Developer (Aug 1995 - Jan 1998) +

+ Maintenance and development of internal report-generation + systems and desktop support. +

-
- -
-
-
-

Small Business Consultant

- Jan 2000 - Dec 2018 -
-
Freelance Consultancy
-

- Provided strategic IT support and consultancy to small businesses, - with a focus on modernising legacy workflows through networking, web - design, and hosting solutions. I acted as a technical partner, - helping clients optimise their existing technology stacks while - offering scalable suggestions for future-proofing their digital - presence through cloud technologies. -

-
- PHP - MySQL - HTML/CSS - JavaScript - WordPress - Linux / Windows - Cloud Technologies -
+
+ PHP (Laravel) + Angular (5+) + SQL (SQL Server) + Linux (Apache/NGINX) + VMWare + VB6 / VBA + C/C++ + Windows Server
-
-
-

Education

-
- -
-

- University of East - Anglia -

-

BSc Honours, Computing Science | 1992 - 1995

-

Grade: 2:1

-

Activities: UEA Korfball Club

-
- -
-

Barnsley College

-

A levels | 1990 - 1992

-

- Computer Studies (A); Electronic Systems (A); Mathematics with - Statistics (C) -

-
- -
-

- The Foulstone School -

-

GCSEs | 1985 - 1990

+ +
+
+
+

Small Business Consultant

+ Jan 2000 - Dec 2018 +
+
Freelance Consultancy

- Eight GCSEs: Mathematics (A), Computer Studies (A), Electronics (A), - Geography (A), Dual Award Science (A,B), English (C) and French (C). + Provided strategic IT support and consultancy to small businesses, + with a focus on modernising legacy workflows through networking, web + design, and hosting solutions. I acted as a technical partner, + helping clients optimise their existing technology stacks while + offering scalable suggestions for future-proofing their digital + presence through cloud technologies.

+
+ PHP + MySQL + HTML/CSS + JavaScript + WordPress + Linux / Windows + Cloud Technologies +
-
+
+
-
-

Interests

-
-
-

Korfball

-

- Fully qualified Level II coach and regular player. Active in the Norfolk - Korfball Association. -

-
-
-

Photography

-

- Landscape photographer capturing the beauty of the Norfolk countryside - through a professional lens. -

-
-
-

Cycling

-

- Avid cyclist exploring the scenic routes, coastal paths, and quiet lanes - of East Anglia. -

-
-
-

Pokemon Go

-

Enjoying chasing Pokémon with family on long walks.

-
+
+

Education

+
+ +
+

+ University of East + Anglia +

+

BSc Honours, Computing Science | 1992 - 1995

+

Grade: 2:1

+

Activities: UEA Korfball Club

-
+ +
+

Barnsley College

+

A levels | 1990 - 1992

+

+ Computer Studies (A); Electronic Systems (A); Mathematics with + Statistics (C) +

+
+ +
+

+ The Foulstone School +

+

GCSEs | 1985 - 1990

+

+ Eight GCSEs: Mathematics (A), Computer Studies (A), Electronics (A), + Geography (A), Dual Award Science (A,B), English (C) and French (C). +

+
+
+
-
-
- +
+ + +
+ - -
- - + \ No newline at end of file