380 lines
14 KiB
Svelte
380 lines
14 KiB
Svelte
|
|
<script>
|
||
|
|
import { onMount } from 'svelte';
|
||
|
|
import { absoluteUrl } from '$lib/site';
|
||
|
|
|
||
|
|
export let page;
|
||
|
|
|
||
|
|
const externalImage = absoluteUrl('/assets/lean101-logotipo.png');
|
||
|
|
|
||
|
|
const structuredData =
|
||
|
|
page.slug === 'home'
|
||
|
|
? JSON.stringify({
|
||
|
|
'@context': 'https://schema.org',
|
||
|
|
'@type': 'ProfessionalService',
|
||
|
|
name: 'Lean 101',
|
||
|
|
url: absoluteUrl('/'),
|
||
|
|
image: externalImage,
|
||
|
|
logo: absoluteUrl('/assets/lean101-isotipo.png'),
|
||
|
|
description: page.meta.metaDescription,
|
||
|
|
email: page.meta.contactEmail,
|
||
|
|
areaServed: 'Worldwide',
|
||
|
|
serviceType: [
|
||
|
|
'Process excellence consulting',
|
||
|
|
'Coaching and training',
|
||
|
|
'Digital solutions'
|
||
|
|
]
|
||
|
|
})
|
||
|
|
: null;
|
||
|
|
|
||
|
|
/** @param {string} tone */
|
||
|
|
const serviceTagClass = (tone) => {
|
||
|
|
if (tone === 'orange') return 'service-tag orange';
|
||
|
|
if (tone === 'charcoal') return 'service-tag charcoal';
|
||
|
|
return 'service-tag';
|
||
|
|
};
|
||
|
|
|
||
|
|
/** @param {string} href */
|
||
|
|
const isExternalLink = (href) => href.startsWith('http');
|
||
|
|
|
||
|
|
/** @param {string} body */
|
||
|
|
const toParagraphs = (body) =>
|
||
|
|
String(body || '')
|
||
|
|
.split(/\n\s*\n/)
|
||
|
|
.map((paragraph) => paragraph.trim())
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
onMount(() => {
|
||
|
|
if (!window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const observer = new IntersectionObserver(
|
||
|
|
(entries) => {
|
||
|
|
entries.forEach((entry) => {
|
||
|
|
if (entry.isIntersecting) {
|
||
|
|
entry.target.classList.add('in');
|
||
|
|
observer.unobserve(entry.target);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
{ threshold: 0.1 }
|
||
|
|
);
|
||
|
|
|
||
|
|
document.querySelectorAll('.section, .hero-visual, .cta-wrap').forEach((element) => {
|
||
|
|
element.classList.add('reveal');
|
||
|
|
observer.observe(element);
|
||
|
|
});
|
||
|
|
|
||
|
|
return () => observer.disconnect();
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<svelte:head>
|
||
|
|
<title>{page.meta.pageTitle}</title>
|
||
|
|
<meta name="description" content={page.meta.metaDescription} />
|
||
|
|
<meta name="robots" content="index,follow" />
|
||
|
|
<meta name="theme-color" content="#0070c7" />
|
||
|
|
<link rel="canonical" href={absoluteUrl(page.path)} />
|
||
|
|
<link rel="icon" href="/assets/lean101-isotipo.png" />
|
||
|
|
<link rel="preconnect" href="https://api.fontshare.com" />
|
||
|
|
<link
|
||
|
|
href="https://api.fontshare.com/v2/css?f[]=general-sans@400,500,600,700&display=swap"
|
||
|
|
rel="stylesheet"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<meta property="og:type" content="website" />
|
||
|
|
<meta property="og:title" content={page.meta.pageTitle} />
|
||
|
|
<meta property="og:description" content={page.meta.socialDescription} />
|
||
|
|
<meta property="og:url" content={absoluteUrl(page.path)} />
|
||
|
|
<meta property="og:image" content={externalImage} />
|
||
|
|
|
||
|
|
<meta name="twitter:card" content="summary_large_image" />
|
||
|
|
<meta name="twitter:title" content={page.meta.pageTitle} />
|
||
|
|
<meta name="twitter:description" content={page.meta.socialDescription} />
|
||
|
|
<meta name="twitter:image" content={externalImage} />
|
||
|
|
|
||
|
|
{#if structuredData}
|
||
|
|
<script type="application/ld+json">{structuredData}</script>
|
||
|
|
{/if}
|
||
|
|
</svelte:head>
|
||
|
|
|
||
|
|
<nav class="nav">
|
||
|
|
<div class="nav-inner">
|
||
|
|
<a href="/" class="nav-logo" aria-label="Lean 101 home">
|
||
|
|
<img src="/assets/lean101-isotipo.png" alt="Lean 101" />
|
||
|
|
</a>
|
||
|
|
<ul class="nav-links">
|
||
|
|
{#each page.nav.links as link}
|
||
|
|
<li><a href={link.href}>{link.label}</a></li>
|
||
|
|
{/each}
|
||
|
|
</ul>
|
||
|
|
<a href={page.nav.ctaHref} class="nav-cta">{page.nav.ctaLabel}</a>
|
||
|
|
</div>
|
||
|
|
</nav>
|
||
|
|
|
||
|
|
{#each page.sections as section}
|
||
|
|
{#if section.type === 'hero'}
|
||
|
|
{@const hero = section.data}
|
||
|
|
<header class="hero" id={section.anchor || undefined}>
|
||
|
|
<span class="hero-eyebrow">{hero.eyebrow}</span>
|
||
|
|
<h1>
|
||
|
|
{hero.titleLead}
|
||
|
|
{#if hero.titleEmphasis}
|
||
|
|
<br />
|
||
|
|
<em>{hero.titleEmphasis}</em>
|
||
|
|
{/if}
|
||
|
|
</h1>
|
||
|
|
<p class="hero-sub">{hero.subtext}</p>
|
||
|
|
<div class="hero-ctas">
|
||
|
|
<a href={hero.primaryCta.href} class="btn btn-primary">
|
||
|
|
{hero.primaryCta.label} <span class="btn-arrow">→</span>
|
||
|
|
</a>
|
||
|
|
<a href={hero.secondaryCta.href} class="btn btn-secondary">
|
||
|
|
{hero.secondaryCta.label}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="hero-visual">
|
||
|
|
<img src={hero.image.src} alt={hero.image.alt} />
|
||
|
|
{#if hero.stats?.length}
|
||
|
|
<div class="hero-stats">
|
||
|
|
{#each hero.stats as stat}
|
||
|
|
<div class="hero-stat">
|
||
|
|
<div class="hero-stat-label">{stat.label}</div>
|
||
|
|
<div class={`hero-stat-num ${stat.tone}`}>{stat.value}</div>
|
||
|
|
</div>
|
||
|
|
{/each}
|
||
|
|
</div>
|
||
|
|
{/if}
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
{:else if section.type === 'services'}
|
||
|
|
{@const block = section.data}
|
||
|
|
<section class="section" id={section.anchor || undefined}>
|
||
|
|
<div class="section-inner">
|
||
|
|
<div class="services-head">
|
||
|
|
<div class="section-eyebrow">{block.eyebrow}</div>
|
||
|
|
<h2 class="section-title">{block.title}</h2>
|
||
|
|
<p class="section-lede">{block.lede}</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="services-grid">
|
||
|
|
{#each block.cards as card}
|
||
|
|
<article class="service-card">
|
||
|
|
<div class="service-img">
|
||
|
|
<span class={serviceTagClass(card.tagTone)}>{card.tag}</span>
|
||
|
|
<img src={card.image.src} alt={card.image.alt} />
|
||
|
|
</div>
|
||
|
|
<div class="service-body">
|
||
|
|
<h3 class="service-title">{card.title}</h3>
|
||
|
|
<p class="service-desc">{card.description}</p>
|
||
|
|
</div>
|
||
|
|
</article>
|
||
|
|
{/each}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
{:else if section.type === 'process'}
|
||
|
|
{@const block = section.data}
|
||
|
|
<section class="section process" id={section.anchor || undefined}>
|
||
|
|
<div class="section-inner">
|
||
|
|
<div class="services-head">
|
||
|
|
<div class="section-eyebrow">{block.eyebrow}</div>
|
||
|
|
<h2 class="section-title">{block.title}</h2>
|
||
|
|
<p class="section-lede">{block.lede}</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flow">
|
||
|
|
{#each block.steps as step, index}
|
||
|
|
<div class="flow-step">
|
||
|
|
<div class="flow-step-label">{step.label}</div>
|
||
|
|
<h3 class="flow-step-title">{step.title}</h3>
|
||
|
|
<p class="flow-step-desc">{step.description}</p>
|
||
|
|
<div class="flow-step-outcome">{step.outcome}</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{#if index < block.steps.length - 1}
|
||
|
|
<div class="flow-arrow" aria-hidden="true">
|
||
|
|
<svg
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
fill="none"
|
||
|
|
stroke="currentColor"
|
||
|
|
stroke-width="2"
|
||
|
|
stroke-linecap="round"
|
||
|
|
stroke-linejoin="round"
|
||
|
|
>
|
||
|
|
<path d="M5 12h14" />
|
||
|
|
<path d="m13 6 6 6-6 6" />
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
{/if}
|
||
|
|
{/each}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
{:else if section.type === 'outcomes'}
|
||
|
|
{@const block = section.data}
|
||
|
|
<section class="section outcomes" id={section.anchor || undefined}>
|
||
|
|
<div class="section-inner">
|
||
|
|
<div class="outcomes-head">
|
||
|
|
<div class="section-eyebrow">{block.eyebrow}</div>
|
||
|
|
<h2 class="section-title">{block.title}</h2>
|
||
|
|
<p class="section-lede">{block.lede}</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="levers-grid">
|
||
|
|
{#each block.items as item, index}
|
||
|
|
<div class="lever">
|
||
|
|
<div class="lever-icon">
|
||
|
|
{#if index === 0}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" /></svg>
|
||
|
|
{:else if index === 1}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" /><path d="m9 12 2 2 4-4" /></svg>
|
||
|
|
{:else if index === 2}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="22" /><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" /></svg>
|
||
|
|
{:else if index === 3}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" y1="9" x2="9.01" y2="9" /><line x1="15" y1="9" x2="15.01" y2="9" /></svg>
|
||
|
|
{:else}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></svg>
|
||
|
|
{/if}
|
||
|
|
</div>
|
||
|
|
<span class="lever-num">{item.number}</span>
|
||
|
|
<h3>{item.title}</h3>
|
||
|
|
<p>{item.description}</p>
|
||
|
|
</div>
|
||
|
|
{/each}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
{:else if section.type === 'why'}
|
||
|
|
{@const block = section.data}
|
||
|
|
<section class="section whyus" id={section.anchor || undefined}>
|
||
|
|
<div class="section-inner">
|
||
|
|
<div class="whyus-layout">
|
||
|
|
<div>
|
||
|
|
<div class="section-eyebrow">{block.eyebrow}</div>
|
||
|
|
<h2 class="section-title">{block.title}</h2>
|
||
|
|
<p class="section-lede">{block.lede}</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<ul class="whyus-list">
|
||
|
|
{#each block.items as item, index}
|
||
|
|
<li class="whyus-item">
|
||
|
|
<div class="whyus-icon">
|
||
|
|
{#if index === 0}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /><path d="M2 12h20" /></svg>
|
||
|
|
{:else if index === 1}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.4 2.4 0 0 1 0-3.4l2.6-2.6a2.4 2.4 0 0 1 3.4 0Z" /><path d="m14.5 12.5 2-2" /><path d="m11.5 9.5 2-2" /><path d="m8.5 6.5 2-2" /><path d="m17.5 15.5 2-2" /></svg>
|
||
|
|
{:else if index === 2}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 10v6M2 10l10-5 10 5-10 5z" /><path d="M6 12v5c3 3 9 3 12 0v-5" /></svg>
|
||
|
|
{:else}
|
||
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" x2="12" y1="20" y2="10" /><line x1="18" x2="18" y1="20" y2="4" /><line x1="6" x2="6" y1="20" y2="16" /></svg>
|
||
|
|
{/if}
|
||
|
|
</div>
|
||
|
|
<div class="whyus-text">
|
||
|
|
<h4>{item.title}</h4>
|
||
|
|
<p>{item.description}</p>
|
||
|
|
</div>
|
||
|
|
</li>
|
||
|
|
{/each}
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
{:else if section.type === 'richText'}
|
||
|
|
{@const block = section.data}
|
||
|
|
<section class="section generic-copy" id={section.anchor || undefined}>
|
||
|
|
<div class="section-inner generic-copy-inner">
|
||
|
|
{#if block.eyebrow}
|
||
|
|
<div class="section-eyebrow">{block.eyebrow}</div>
|
||
|
|
{/if}
|
||
|
|
{#if block.title}
|
||
|
|
<h2 class="section-title">{block.title}</h2>
|
||
|
|
{/if}
|
||
|
|
<div class="generic-copy-body">
|
||
|
|
{#each toParagraphs(block.body) as paragraph}
|
||
|
|
<p>{paragraph}</p>
|
||
|
|
{/each}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
{:else if section.type === 'cta'}
|
||
|
|
{@const block = section.data}
|
||
|
|
<section class="cta-wrap" id={section.anchor || undefined}>
|
||
|
|
<div class="cta-card">
|
||
|
|
<div class="cta-inner">
|
||
|
|
<h2>
|
||
|
|
{block.titleLead} <em>{block.titleEmphasis}</em> {block.titleTail}
|
||
|
|
</h2>
|
||
|
|
<p>{block.body}</p>
|
||
|
|
|
||
|
|
<div class="cta-flow">
|
||
|
|
{#each block.flowSteps as step, index}
|
||
|
|
<div class="cta-flow-step"><span class="num">{index + 1}</span> {step}</div>
|
||
|
|
{#if index < block.flowSteps.length - 1}
|
||
|
|
<span class="cta-flow-arrow">→</span>
|
||
|
|
{/if}
|
||
|
|
{/each}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="hero-ctas">
|
||
|
|
<a href={block.primaryCta.href} class="btn btn-on-dark">
|
||
|
|
{block.primaryCta.label} <span class="btn-arrow">→</span>
|
||
|
|
</a>
|
||
|
|
<a href={block.secondaryCta.href} class="btn btn-ghost-dark">
|
||
|
|
{block.secondaryCta.label}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
{/if}
|
||
|
|
{/each}
|
||
|
|
|
||
|
|
<footer class="footer">
|
||
|
|
<div class="footer-inner">
|
||
|
|
<div class="footer-grid">
|
||
|
|
<div class="footer-brand">
|
||
|
|
<img src="/assets/lean101-logotipo.png" alt="Lean 101" />
|
||
|
|
<p>{page.footer.brandTagline}</p>
|
||
|
|
</div>
|
||
|
|
<div class="footer-col">
|
||
|
|
<h5>Services</h5>
|
||
|
|
<ul>
|
||
|
|
{#each page.footer.servicesLinks as link}
|
||
|
|
<li><a href={link.href}>{link.label}</a></li>
|
||
|
|
{/each}
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
<div class="footer-col">
|
||
|
|
<h5>Company</h5>
|
||
|
|
<ul>
|
||
|
|
{#each page.footer.companyLinks as link}
|
||
|
|
<li><a href={link.href}>{link.label}</a></li>
|
||
|
|
{/each}
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
<div class="footer-col">
|
||
|
|
<h5>Connect</h5>
|
||
|
|
<ul>
|
||
|
|
{#each page.footer.connectLinks as link}
|
||
|
|
<li>
|
||
|
|
<a
|
||
|
|
href={link.href}
|
||
|
|
target={isExternalLink(link.href) ? '_blank' : undefined}
|
||
|
|
rel={isExternalLink(link.href) ? 'noreferrer' : undefined}
|
||
|
|
>
|
||
|
|
{link.label}
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
{/each}
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="footer-bottom">
|
||
|
|
<span>© {page.meta.copyrightYear} Lean 101. All rights reserved.</span>
|
||
|
|
<span>{page.meta.location}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</footer>
|