Files
skillfactor-pipeline/gitea-custom/templates/home.tmpl
skillfactor-pipeline 8ea7342414 feat(site): add #integration section (Works with Zeiterfassung.CLOUD)
Placed after 'One agent per employee', before 'Safe sharing'. Three tiles
(many professions incl. hobby / hobby skills with provenance / matching),
Mustermann example card with two loaded package chips, verbatim JSON
snippet with all relation_type values, ESCO/O*NET/KldB mapping line,
explorer CTA (?de=1&vendor=skillfactor, target as TODO placeholder).
Validated: JSON schemas, acceptance checks, headless render 0 console
errors, tiles stack on mobile via existing grid rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PDKeXvpT6tENSvyQGLV1Uq
2026-07-11 20:00:29 +02:00

767 lines
53 KiB
Cheetah
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{template "base/head" .}}
<style>
/* skillfactor landing — Factorial design language (DM Sans, #FF355E, pills, 16px cards) */
.sf-page { font-family: "DM Sans", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; color: #25253d; background: #fff; }
.sf-wrap { max-width: 1160px; margin: 0 auto; padding: 0 24px; }
.sf-eyebrow { display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: #ff355e; margin-bottom: 18px; }
.sf-h1 { font-size: 60px; line-height: 1.05; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 22px; color: #25253d; }
.sf-h1 em { font-style: normal; color: #ff355e; }
.sf-h2 { font-size: 38px; line-height: 1.15; font-weight: 700; letter-spacing: -0.01em; margin: 0 0 14px; color: #25253d; }
.sf-lead { font-size: 19px; line-height: 1.55; color: #515164; max-width: 560px; margin: 0 0 30px; }
.sf-sub { font-size: 17px; line-height: 1.6; color: #515164; max-width: 640px; margin: 0 0 34px; }
.sf-btn { display: inline-flex; align-items: center; gap: 8px; border-radius: 9999px; padding: 13px 26px; font-size: 16px; font-weight: 500; text-decoration: none; transition: background .15s ease, color .15s ease; }
.sf-btn-primary { background: #ff355e; color: #fff !important; }
.sf-btn-primary:hover { background: #e51943; }
.sf-btn-ghost { background: #fff; color: #25253d !important; box-shadow: inset 0 0 0 1px #dededf; }
.sf-btn-ghost:hover { background: #f4f4f5; }
.sf-btn-white { background: #fff; color: #e51943 !important; }
.sf-btn-white:hover { background: #ffe0e6; }
.sf-tlink { color: #007c85 !important; font-weight: 500; text-decoration: none; }
.sf-tlink:hover { color: #00565c !important; }
/* Hero */
.sf-hero { padding: 84px 0 56px; }
.sf-hero-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 56px; align-items: center; }
.sf-cta-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
/* Dark package card (from the pitch) */
.sf-code-card { background: #25253d; border-radius: 20px; padding: 30px 34px; color: #eef0f4; box-shadow: 0 24px 60px rgba(37,37,61,.22); position: relative; }
.sf-code-card .sf-code-label { font-size: 11px; letter-spacing: .18em; color: #8a8a99; margin-bottom: 16px; text-transform: uppercase; }
.sf-code-card pre { margin: 0; font: 15px/1.75 ui-monospace, SFMono-Regular, Consolas, monospace; color: #eef0f4; background: none; white-space: pre; }
.sf-code-card pre b { color: #ff6682; font-weight: 600; }
.sf-code-card pre i { color: #63d3db; font-style: normal; }
.sf-chip { position: absolute; background: #fff; border-radius: 9999px; padding: 9px 16px; font-size: 14px; font-weight: 700; color: #25253d; box-shadow: 0 10px 28px rgba(37,37,61,.16); }
.sf-chip small { display: block; font-size: 11px; font-weight: 500; color: #8a8a99; }
.sf-chip-1 { top: -18px; right: 26px; }
.sf-chip-2 { bottom: -22px; left: -28px; }
.sf-chip-3 { top: 46%; right: -30px; }
.sf-chip em { font-style: normal; color: #ff355e; }
/* Stats band */
.sf-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; padding: 34px 0 8px; }
.sf-stat { text-align: center; padding: 18px 10px; }
.sf-stat b { display: block; font-size: 34px; font-weight: 700; color: #25253d; letter-spacing: -0.02em; margin-bottom: 12px; }
.sf-stat span { display: block; font-size: 14px; color: #515164; line-height: 1.45; }
/* Sections */
.sf-section { padding: 76px 0; }
.sf-section-gray { background: #f4f4f5; border-radius: 28px; }
.sf-center { text-align: center; }
.sf-center .sf-sub { margin-left: auto; margin-right: auto; }
/* Formula */
.sf-formula { display: grid; grid-template-columns: 1fr auto 1fr auto 1fr; gap: 14px; align-items: stretch; margin: 40px 0 20px; }
.sf-fcard { background: #fff; border: 1px solid #ececee; border-radius: 16px; padding: 26px; }
.sf-fcard h3 { margin: 0 0 8px; font-size: 19px; color: #ff355e; font-weight: 700; }
.sf-fcard p { margin: 0; font-size: 15px; line-height: 1.55; color: #515164; }
.sf-fop { align-self: center; font-size: 34px; font-weight: 700; color: #ff355e; }
.sf-fresult { margin-top: 6px; background: #ff355e; color: #fff; border-radius: 16px; padding: 20px 26px; text-align: center; font-size: 19px; font-weight: 700; }
/* Feature cards */
.sf-features { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin-top: 40px; }
.sf-feat { background: #fff; border: 1px solid #ececee; border-radius: 16px; padding: 26px; }
.sf-feat .sf-ico { width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 20px; margin-bottom: 16px; background: #fff0f3; }
.sf-feat h3 { margin: 0 0 8px; font-size: 17px; font-weight: 700; color: #25253d; }
.sf-feat p { margin: 0; font-size: 14.5px; line-height: 1.55; color: #515164; }
/* Layer model */
.sf-layers-grid { display: grid; grid-template-columns: 1.3fr .7fr; gap: 26px; margin-top: 40px; align-items: start; }
.sf-layer { color: #fff; border-radius: 14px; padding: 18px 24px; display: flex; justify-content: space-between; align-items: baseline; gap: 18px; margin-bottom: 12px; }
.sf-layer b { font-size: 18px; }
.sf-layer span { font-size: 14.5px; opacity: .92; text-align: right; }
.sf-gate { background: #fff0f3; border-radius: 16px; padding: 26px; }
.sf-gate h3 { margin: 0 0 14px; font-size: 18px; color: #25253d; }
.sf-gate ul { margin: 0; padding: 0; list-style: none; }
.sf-gate li { font-size: 14.5px; color: #515164; line-height: 1.5; padding: 7px 0 7px 30px; position: relative; }
.sf-gate li::before { content: "✓"; position: absolute; left: 0; top: 7px; width: 20px; height: 20px; border-radius: 50%; background: #ff355e; color: #fff; font-size: 12px; display: flex; align-items: center; justify-content: center; }
/* Pipeline steps */
.sf-steps { margin-top: 40px; }
.sf-step { display: grid; grid-template-columns: 64px 220px 1fr; gap: 18px; align-items: center; background: #fff; border: 1px solid #ececee; border-radius: 16px; padding: 18px 24px; margin-bottom: 12px; }
.sf-step .sf-num { font-size: 22px; font-weight: 700; color: #ff355e; }
.sf-step b { font-size: 17px; color: #25253d; }
.sf-step span { font-size: 14.5px; color: #515164; }
/* CTA band */
.sf-cta-band { background: linear-gradient(120deg, #ff355e, #e51943); border-radius: 28px; color: #fff; text-align: center; padding: 70px 40px; }
.sf-cta-band h2 { font-size: 40px; font-weight: 700; letter-spacing: -0.01em; margin: 0 0 12px; color: #fff; }
.sf-cta-band p { font-size: 18px; opacity: .95; margin: 0 auto 30px; max-width: 560px; }
.sf-footnote { padding: 34px 0 60px; font-size: 12.5px; color: #8a8a99; text-align: center; }
/* Integration (Zeiterfassung.CLOUD) */
.sf-int-grid { display: grid; grid-template-columns: .9fr 1.1fr; gap: 26px; margin-top: 36px; align-items: start; }
.sf-int-person { background: #fff; border: 1px solid #ececee; border-radius: 16px; padding: 24px 26px; }
.sf-int-name { font-size: 19px; font-weight: 700; color: #25253d; margin-bottom: 10px; }
.sf-int-role { font-size: 14px; color: #515164; padding: 6px 0; border-bottom: 1px solid #f4f4f5; }
.sf-int-role code { background: #fff0f3; color: #b30d32; border-radius: 6px; padding: 1px 7px; font-size: 12.5px; }
.sf-int-loads { margin: 14px 0 10px; font-size: 11.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #8a8a99; }
.sf-int-chips { display: flex; flex-direction: column; gap: 8px; }
.sf-int-chip { display: block; text-decoration: none; font: 500 13px/1.5 ui-monospace, Consolas, monospace; color: #25253d !important; background: #f4f4f5; border: 1px solid #ececee; border-radius: 10px; padding: 9px 13px; }
.sf-int-chip small { color: #8a8a99; font-family: "DM Sans", sans-serif; margin-left: 6px; }
.sf-int-chip:hover { border-color: #ff355e; }
.sf-int-chip-main { background: #fff0f3; border-color: #ffd6de; }
.sf-int-chip-main small { color: #b30d32; }
@media (max-width: 1000px) { .sf-int-grid { grid-template-columns: 1fr; } }
/* Live-Status-Panel */
.sf-live-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.sf-live-card { background: #fff; border: 1px solid #ffd6de; border-radius: 12px; padding: 14px 16px; display: block; }
a.sf-live-card:hover { border-color: #ff355e; box-shadow: 0 8px 24px rgba(255,53,94,.12); }
.sf-live-num { font-size: 26px; font-weight: 800; color: #ff355e; letter-spacing: -0.02em; }
.sf-live-num small { font-size: 14px; color: #8a8a99; font-weight: 600; }
.sf-live-label { font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: #25253d; margin: 4px 0 8px; }
.sf-live-card p { margin: 0; font-size: 12.5px; line-height: 1.5; color: #515164; }
.sf-live-card p b { color: #25253d; }
@media (max-width: 1000px) { .sf-live-grid { grid-template-columns: repeat(2, 1fr); } }
/* Chat-Beispiel (Mitmach-Sektion) — Claude-Chat-Optik mit Bubbles + Avataren */
.sf-chat { max-width: 880px; margin: 36px auto 0; display: flex; flex-direction: column; gap: 18px;
background: #fff; border: 1px solid #ececee; border-radius: 22px; padding: 26px 22px;
box-shadow: 0 14px 44px rgba(37,37,61,.08); }
.sf-row { display: flex; gap: 10px; align-items: flex-end; }
.sf-row-user { justify-content: flex-end; }
.sf-row-ai { justify-content: flex-start; }
.sf-avatar { flex: none; width: 34px; height: 34px; border-radius: 50%; display: flex;
align-items: center; justify-content: center; font-size: 11px; font-weight: 800; }
.sf-avatar-user { background: #25253d; color: #fff; }
.sf-avatar-ai { background: #d97757; color: #fff; font-size: 16px; }
.sf-bubble { border-radius: 18px; padding: 14px 18px; font-size: 15px; line-height: 1.6; max-width: 78%; }
.sf-bubble p { margin: 0 0 10px; }
.sf-bubble p:last-child { margin-bottom: 0; }
.sf-bubble-user { background: #ff355e; color: #fff; border-bottom-right-radius: 5px; }
.sf-bubble-user a, .sf-bubble-user em { color: #ffe0e6; }
.sf-bubble-ai { background: #f4f4f5; color: #25253d; border-bottom-left-radius: 5px; max-width: 86%; }
.sf-prompt-copy { display: flex; gap: 10px; align-items: center; background: rgba(255,255,255,.14); border: 1.5px dashed rgba(255,255,255,.6); border-radius: 10px; padding: 10px 14px; }
.sf-prompt-copy code { font: 600 14.5px/1.5 ui-monospace, Consolas, monospace; color: #fff; }
.sf-copy-btn { flex: none; border: 0; border-radius: 9999px; background: #fff; color: #e51943; font-size: 12px; font-weight: 700; padding: 6px 12px; cursor: pointer; }
.sf-prompt-variants { margin-top: 10px; font-size: 12.5px; opacity: .92; }
.sf-distillate { background: #fff; border: 1px solid #ececee; border-radius: 12px; margin: 12px 0; overflow: hidden; }
.sf-dist-head { background: #fff0f3; color: #b30d32; font-size: 12.5px; font-weight: 700; padding: 8px 14px; }
.sf-dist-head span { color: #8a8a99; font-weight: 500; font-family: ui-monospace, Consolas, monospace; margin-left: 8px; }
.sf-distillate pre { margin: 0; padding: 14px 16px; font: 13px/1.6 ui-monospace, Consolas, monospace; white-space: pre-wrap; color: #25253d; background: none; }
@media (max-width: 700px) { .sf-bubble { max-width: 100%; } .sf-avatar { display: none; } }
/* --- Motion: Einstieg, Scroll-Reveals, Float, Donut --- */
@keyframes sf-rise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
.sf-hero .sf-eyebrow { animation: sf-rise .6s ease both; }
.sf-hero .sf-h1 { animation: sf-rise .7s .08s ease both; }
.sf-hero .sf-lead { animation: sf-rise .7s .16s ease both; }
.sf-hero .sf-cta-row { animation: sf-rise .7s .24s ease both; }
.sf-hero .sf-code-card { animation: sf-rise .8s .2s ease both; }
.sf-reveal { opacity: 0; transform: translateY(26px); transition: opacity .7s ease, transform .7s ease; }
.sf-reveal.sf-in { opacity: 1; transform: none; }
/* Richtungs-/Zoom-Varianten fuer einzelne Elemente */
.sf-reveal-l { opacity: 0; transform: translateX(-42px); transition: opacity .7s ease, transform .7s cubic-bezier(.2,.7,.3,1); }
.sf-reveal-r { opacity: 0; transform: translateX(42px); transition: opacity .7s ease, transform .7s cubic-bezier(.2,.7,.3,1); }
.sf-reveal-z { opacity: 0; transform: scale(.92); filter: blur(6px); transition: opacity .8s ease, transform .8s cubic-bezier(.2,.7,.3,1), filter .8s ease; }
.sf-reveal-l.sf-in, .sf-reveal-r.sf-in { opacity: 1; transform: none; }
.sf-reveal-z.sf-in { opacity: 1; transform: none; filter: none; }
/* Stagger ueber data-delay (JS setzt transition-delay) + Grid-Staffelung */
.sf-features .sf-feat:nth-child(2), .sf-formula .sf-fcard:nth-child(3) { transition-delay: .1s; }
.sf-features .sf-feat:nth-child(3), .sf-formula .sf-fcard:nth-child(5) { transition-delay: .2s; }
.sf-features .sf-feat:nth-child(4) { transition-delay: .3s; }
.sf-steps .sf-step:nth-child(2) { transition-delay: .08s; }
.sf-steps .sf-step:nth-child(3) { transition-delay: .16s; }
.sf-steps .sf-step:nth-child(4) { transition-delay: .24s; }
.sf-steps .sf-step:nth-child(5) { transition-delay: .32s; }
.sf-layers-grid .sf-layer:nth-child(2) { transition-delay: .1s; }
.sf-layers-grid .sf-layer:nth-child(3) { transition-delay: .2s; }
.sf-layers-grid .sf-layer:nth-child(4) { transition-delay: .3s; }
.sf-stats .sf-stat:nth-child(2) { transition-delay: .1s; }
.sf-stats .sf-stat:nth-child(3) { transition-delay: .2s; }
.sf-stats .sf-stat:nth-child(4) { transition-delay: .3s; }
/* Sektions-Header gleiten sanft nach */
.sf-center .sf-eyebrow.sf-reveal { transition-delay: 0s; }
.sf-center .sf-h2.sf-reveal { transition-delay: .08s; }
.sf-center .sf-sub.sf-reveal { transition-delay: .16s; }
@keyframes sf-float { 0%, 100% { margin-top: 0; } 50% { margin-top: -9px; } }
.sf-chip-1 { animation: sf-float 5s ease-in-out infinite; }
.sf-chip-2 { animation: sf-float 6s 1.2s ease-in-out infinite; }
.sf-chip-3 { animation: sf-float 5.5s .6s ease-in-out infinite; }
.sf-feat, .sf-fcard, .sf-step { transition: transform .25s ease, box-shadow .25s ease, opacity .7s ease; }
.sf-feat:hover, .sf-fcard:hover, .sf-step:hover { transform: translateY(-4px); box-shadow: 0 16px 40px rgba(37,37,61,.10); }
/* Provenienz-Donut */
.sf-prov-grid { display: grid; grid-template-columns: .9fr 1.1fr; gap: 56px; align-items: center; margin-top: 44px; }
.sf-donut-wrap { position: relative; width: 340px; max-width: 100%; margin: 0 auto; }
.sf-donut { width: 100%; height: auto; transform: rotate(-90deg); }
.sf-donut circle { fill: none; stroke-width: 26; stroke-dasharray: 0 100; transition: stroke-dasharray 1.3s cubic-bezier(.6,.1,.3,1); }
.sf-in .sf-donut circle { stroke-dasharray: var(--seg); }
.sf-donut-center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.sf-donut-center b { font-size: 28px; color: #25253d; }
.sf-donut-center span { font-size: 13px; color: #8a8a99; max-width: 140px; }
.sf-legend { list-style: none; margin: 0; padding: 0; }
.sf-legend li { display: flex; align-items: baseline; gap: 12px; padding: 12px 0; border-bottom: 1px solid #ececee; }
.sf-legend .sf-dot { width: 14px; height: 14px; border-radius: 4px; flex: none; transform: translateY(2px); }
.sf-legend b { font-size: 16px; color: #25253d; }
.sf-legend .sf-pct { margin-left: auto; font-size: 18px; font-weight: 700; color: #25253d; }
.sf-legend p { margin: 2px 0 0; font-size: 13.5px; color: #515164; }
@media (max-width: 1000px) {
.sf-h1 { font-size: 42px; }
.sf-hero-grid { grid-template-columns: 1fr; }
.sf-stats, .sf-features { grid-template-columns: repeat(2, 1fr); }
.sf-formula { grid-template-columns: 1fr; }
.sf-fop { text-align: center; }
.sf-layers-grid { grid-template-columns: 1fr; }
.sf-step { grid-template-columns: 48px 1fr; }
.sf-step span { grid-column: 2; }
}
</style>
<div role="main" aria-label="skillfactor" class="page-content home sf-page">
<!-- Hero -->
<section class="sf-hero">
<div class="sf-wrap sf-hero-grid">
<div>
<span class="sf-eyebrow">The Occupational Skill Layer</span>
<h1 class="sf-h1">AI with <em>work experience.</em></h1>
<p class="sf-lead">A standard agent knows nothing about recruiting, accounting or project work.
skillfactor compiles public occupation data, real job ads and expert knowledge into a
ready-to-use skill per profession — a git-native library that gets better with every project.
Built for computer-based work: the 1,953 white-collar professions come first.</p>
<div class="sf-cta-row">
<a class="sf-btn sf-btn-primary" href="{{AppSubUrl}}/skills-core">Explore the skill catalog</a>
<a class="sf-btn sf-btn-ghost" href="#sf-formula">How it works</a>
</div>
</div>
<div>
<div class="sf-code-card">
<div class="sf-code-label">Skill package — every source compiled</div>
<pre><b>artificial-intelligence-engineer/</b>
├─ SKILL.md
├─ references/
│ ├─ profile · tasks · skills
│ ├─ market.md <i>← live job-ad evidence</i>
│ ├─ ai-skills.md <i>← <span data-stat="flagship_ai_skills">…</span> tiered agent skills</i>
│ ├─ practitioner-qa.md <i>← Stack Exchange</i>
│ ├─ usecases · intake · quality
│ └─ glossary · literature
├─ evals/
└─ PROVENANCE.md</pre>
<div class="sf-chip sf-chip-1">all <em>6 sources</em><small>ESCO · O*NET · job ads · experts · skill libs · Stack Exchange</small></div>
<div class="sf-chip sf-chip-2"><em data-stat="flagship_ai_skills">…</em> agent skills<small>tiered &amp; mapped from open-source libraries</small></div>
<div class="sf-chip sf-chip-3">every file <em>clickable</em><small>rendered live in the repo</small></div>
</div>
</div>
</div>
</section>
<!-- Live processing status (polls assets/status.json every 5 min) -->
<section>
<div class="sf-wrap">
<div id="sf-live-status" style="background:#fff0f3;border:1px solid #ffd6de;border-radius:16px;padding:20px 22px;">
<div style="display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:8px;margin-bottom:14px;">
<b style="color:#25253d;font-size:16px;">⚡ Live build status</b>
<span style="color:#8a8a99;font-size:12.5px;">as of <span data-st="updated">…</span> · auto-refreshes every 5 min</span>
</div>
<div class="sf-live-grid">
<a class="sf-live-card" href="{{AppSubUrl}}/assets/processed.html" style="text-decoration:none;">
<div class="sf-live-num"><span data-st="done">…</span><small> / <span data-st="total">3,039</span></small></div>
<div class="sf-live-label">occupations with market evidence →</div>
<p>Every processed occupation gets its skill rankings from real, relevance-checked job ads — click to browse the finished ones.</p>
</a>
<div class="sf-live-card">
<div class="sf-live-num"><span data-st="percent">…</span><small>%</small></div>
<div class="sf-live-label">of the catalog crawled</div>
<p>The crawl started <b data-st="started">…</b> and has been running for <b data-st="elapsed">…</b> — computer-based professions first.</p>
</div>
<div class="sf-live-card">
<div class="sf-live-num" data-st="ollama">…</div>
<div class="sf-live-label">job ads read by our own AI</div>
<p>Each ad is distilled by a self-hosted model (gemma3 on our GPU — no data leaves the house) into structured facts: skills, tools, seniority. One count = one ad.</p>
</div>
<div class="sf-live-card" id="sf-se-status" style="display:none;">
<div class="sf-live-num" data-st="se_num">…</div>
<div class="sf-live-label" data-st="se_label">knowledge layer</div>
<p data-st="se_text">Practitioner knowledge from Stack Exchange, curated per profession.</p>
</div>
<div class="sf-live-card" id="sf-eta-card" style="display:none;">
<div class="sf-live-num" data-st="eta_date">…</div>
<div class="sf-live-label">estimated completion</div>
<p>Projected from the current pace (≈<b data-st="eta_days">…</b> days to go): all 3,039 occupations crawled, every package re-published with its full market evidence, plus the final coverage report.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Stats band -->
<section>
<div class="sf-wrap sf-stats">
<div class="sf-stat"><b data-count="1953" data-stat="white_collar">0</b><span>white-collar professions — where the computer is the workplace</span></div>
<div class="sf-stat"><b data-count="3039" data-stat="total_occupations">0</b><span>occupations in the full catalog</span></div>
<div class="sf-stat"><b data-count="126051" data-stat="occupation_skill_relations">0</b><span>occupation-to-skill relations</span></div>
<div class="sf-stat"><b data-count="902" data-stat="external_skills">0</b><span>agent skills mapped in from <span data-stat="external_sources">16</span> open-source skill libraries</span></div>
</div>
</section>
<!-- Formula -->
<section class="sf-section" id="sf-formula">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">The formula</span>
<h2 class="sf-h2">Public occupation knowledge becomes a skill</h2>
<p class="sf-sub">Occupational knowledge belongs to nobody — so nobody maintains it.
skillfactor changes that: three sources, one compilation, versioned in git.</p>
</div>
<div class="sf-formula">
<div class="sf-fcard"><h3>Occupation taxonomies</h3><p>3,000+ occupations from ESCO &amp; O*NET — public, standardized, multilingual. Joined via the official crosswalk.</p></div>
<div class="sf-fop">+</div>
<div class="sf-fcard"><h3>Job ads</h3><p>Current skill requirements from global job platforms — weighted market evidence with percentages and an as-of date.</p></div>
<div class="sf-fop">+</div>
<div class="sf-fcard"><h3>Expert knowledge</h3><p>Best practices from literature and the web, 900+ proven agent skills from 16 open-source libraries (Anthropic, NVIDIA, Google, community) — and curated practitioner Q&amp;A from six Stack Exchange communities, each entry attributed (CC-BY-SA).</p></div>
</div>
<div class="sf-fresult sf-reveal">= AI compiles them into the ready-to-use skill for the profession</div>
</div>
</section>
<!-- Data provenance -->
<section class="sf-section" id="sf-provenance" style="padding-top: 20px;">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">Data provenance</span>
<h2 class="sf-h2">Every item knows where it comes from</h2>
<p class="sf-sub">After compilation, each package reports its source mix.
Shown here: the <a class="sf-tlink" href="{{AppSubUrl}}/skills-core/artificial-intelligence-engineer/src/branch/main/PROVENANCE.md">AI-engineer package</a> with every source compiled —
every repo carries the same breakdown as <code>PROVENANCE.md</code> with a rendered chart.</p>
</div>
<div class="sf-prov-grid sf-reveal" id="sf-prov" data-manifest="{{AppSubUrl}}/skills-core/artificial-intelligence-engineer/raw/branch/main/manifest.json">
<div class="sf-donut-wrap">
<svg class="sf-donut" viewBox="0 0 200 200" aria-label="Content sources of the recruiter package">
<circle cx="100" cy="100" r="80" pathLength="100" stroke="#ff355e" data-cat="jobads" style="--seg: 52.8 47.2;" stroke-dashoffset="0"></circle>
<circle cx="100" cy="100" r="80" pathLength="100" stroke="#1e88e5" data-cat="onet" style="--seg: 30.5 69.5;" stroke-dashoffset="-52.8"></circle>
<circle cx="100" cy="100" r="80" pathLength="100" stroke="#43a047" data-cat="wiki_ai" style="--seg: 9.9 90.1;" stroke-dashoffset="-83.3"></circle>
<circle cx="100" cy="100" r="80" pathLength="100" stroke="#f9a825" data-cat="esco" style="--seg: 6.1 93.9;" stroke-dashoffset="-93.2"></circle>
<circle cx="100" cy="100" r="80" pathLength="100" stroke="#8e24aa" data-cat="ai_skills" style="--seg: 0.7 99.3;" stroke-dashoffset="-99.3"></circle>
<circle cx="100" cy="100" r="80" pathLength="100" stroke="#00acc1" data-cat="stackx" style="--seg: 0 100;" stroke-dashoffset="-100"></circle>
</svg>
<div class="sf-donut-center"><b data-prov-total>&nbsp;</b><span>content items in the AI-engineer package</span></div>
</div>
<ul class="sf-legend">
<li><span class="sf-dot" style="background:#ff355e;"></span><span><b>Job boards — market evidence</b><p>Full market report from real job ads (JSearch API): ranked requirements with share, seniority distribution, title variants — extracted facts only, aggregated live.</p></span><span class="sf-pct" data-pct="jobads">53%</span></li>
<li><span class="sf-dot" style="background:#1e88e5;"></span><span><b>O*NET — tasks &amp; tools</b><p>Task statements, work activities and software from the U.S. occupation database.</p></span><span class="sf-pct" data-pct="onet">31%</span></li>
<li><span class="sf-dot" style="background:#43a047;"></span><span><b>Wikipedia &amp; AI expert curation</b><p>Glossary, literature, use cases, intake questions, quality criteria and evals — AI-curated with cited web sources.</p></span><span class="sf-pct" data-pct="wiki_ai">10%</span></li>
<li><span class="sf-dot" style="background:#f9a825;"></span><span><b>ESCO — occupation &amp; competences</b><p>The European profile: essential and optional competences per occupation.</p></span><span class="sf-pct" data-pct="esco">6%</span></li>
<li><span class="sf-dot" style="background:#8e24aa;"></span><span><b>External AI skill packs — mapped</b><p>Proven agent skills from the top open-source libraries (anthropics/skills, obra/superpowers, wshobson/agents), matched to the occupation — linked with per-source attribution, never copied.</p></span><span class="sf-pct" data-pct="ai_skills">1%</span></li>
<li><span class="sf-dot" style="background:#00acc1;"></span><span><b>Stack Exchange — practitioner Q&amp;A</b><p>What experienced practitioners actually advise: quality-filtered questions and answers from six professional Stack Exchange communities (Workplace, Project Management, Law, Money, Software Engineering, Data Science), condensed into per-profession insights — each entry attributed to its author, CC-BY-SA 4.0.</p></span><span class="sf-pct" data-pct="stackx">0%</span></li>
</ul>
</div>
<p class="sf-center" style="margin-top:26px; font-size:14px; color:#8a8a99;">Numbers load live from the package manifest. The job-ad rollout for all 3,039 occupations is running — packages gain their market-evidence segment as it lands.
<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/marketplace">See the library-wide chart&nbsp;→</a></p>
</div>
</section>
<!-- Example packages -->
<section class="sf-section" id="sf-examples" style="padding-top: 8px;">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">See for yourself</span>
<h2 class="sf-h2">Open a package. Read what the agent knows.</h2>
<p class="sf-sub">Four occupations, one click each — every file renders directly in the repository.</p>
</div>
<div class="sf-features">
<div class="sf-feat" style="border: 2px solid #ff355e; box-shadow: 0 8px 28px rgba(255,53,94,.14);"><div class="sf-ico">🤖</div><h3>AI engineer</h3>
<p>Every source compiled: taxonomy profile, gated job-ad evidence (US + DACH), expert curation, tiered agent skills and Stack Exchange practitioner Q&amp;A.</p>
<p style="margin-top:12px;"><a class="sf-tlink" href="{{AppSubUrl}}/skills-core/artificial-intelligence-engineer/src/branch/main/SKILL.md">SKILL.md&nbsp;→</a><br>
<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/artificial-intelligence-engineer/src/branch/main/references/ai-skills.md">AI skills&nbsp;→</a>&nbsp;·&nbsp;<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/artificial-intelligence-engineer">repo&nbsp;→</a></p></div>
<div class="sf-feat"><div class="sf-ico">⚖️</div><h3>Lawyer</h3>
<p>Legal profile, case-related tasks and competences — plus mapped compliance &amp; contract agent skills.</p>
<p style="margin-top:12px;"><a class="sf-tlink" href="{{AppSubUrl}}/skills-core/lawyer/src/branch/main/SKILL.md">SKILL.md&nbsp;→</a><br>
<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/lawyer/src/branch/main/references/ai-skills.md">AI skills&nbsp;→</a>&nbsp;·&nbsp;<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/lawyer">repo&nbsp;→</a></p></div>
<div class="sf-feat"><div class="sf-ico">🗄️</div><h3>Database administrator</h3>
<p>The deepest AI-skill mapping in the catalog: 90 agent skills for design, migrations, ops and security.</p>
<p style="margin-top:12px;"><a class="sf-tlink" href="{{AppSubUrl}}/skills-core/database-administrator/src/branch/main/SKILL.md">SKILL.md&nbsp;→</a><br>
<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/database-administrator/src/branch/main/references/ai-skills.md">AI skills&nbsp;→</a>&nbsp;·&nbsp;<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/database-administrator">repo&nbsp;→</a></p></div>
<div class="sf-feat"><div class="sf-ico">🎨</div><h3>Web designer</h3>
<p>Design competences plus 49 mapped skills — frontend, accessibility, UI patterns and brand systems.</p>
<p style="margin-top:12px;"><a class="sf-tlink" href="{{AppSubUrl}}/skills-core/web-designer/src/branch/main/SKILL.md">SKILL.md&nbsp;→</a><br>
<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/web-designer/src/branch/main/references/ai-skills.md">AI skills&nbsp;→</a>&nbsp;·&nbsp;<a class="sf-tlink" href="{{AppSubUrl}}/skills-core/web-designer">repo&nbsp;→</a></p></div>
</div>
<p class="sf-center" style="margin-top:26px;"><a class="sf-tlink" href="{{AppSubUrl}}/skills-core/by-industry">Or browse all 3,039 by industry&nbsp;→</a></p>
</div>
</section>
<!-- One agent per employee -->
<section class="sf-section">
<div class="sf-wrap sf-section-gray" style="padding: 64px 48px;">
<div class="sf-center">
<span class="sf-eyebrow">The product</span>
<h2 class="sf-h2">One agent per employee — preloaded with the profession</h2>
<p class="sf-sub">The agent knows the job from day one — the employee makes it their own.
A sparring partner that gets better every day and never quits.</p>
</div>
<div class="sf-features">
<div class="sf-feat"><div class="sf-ico">🏭</div><h3>Preloaded</h3><p>Starts with the skill profile of the profession — from ESCO, O*NET and market data.</p></div>
<div class="sf-feat"><div class="sf-ico">📂</div><h3>Knows the day-to-day</h3><p>Mails, chats, documents, meeting transcripts — the personal work context, every day.</p></div>
<div class="sf-feat"><div class="sf-ico">🎓</div><h3>Grows with you</h3><p>Every task and every correction improves it; hidden abilities surface in the skill graph.</p></div>
<div class="sf-feat"><div class="sf-ico">🔒</div><h3>Stays in the company</h3><p>Knowledge stays with the role — even when the person leaves. The role never forgets.</p></div>
</div>
</div>
</section>
<!-- Integration: Zeiterfassung.CLOUD HR API -->
<section class="sf-section" id="integration" style="padding-top: 8px;">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">Integration</span>
<h2 class="sf-h2">Works with Zeiterfassung.CLOUD</h2>
<p class="sf-sub">The HR API knows every profession a person has — practiced, trained, or just loved.
The agent loads them all.</p>
</div>
<div class="sf-features" style="grid-template-columns: repeat(3, 1fr);">
<div class="sf-feat"><div class="sf-ico">👥</div><h3>Many professions, one person</h3>
<p><code>relation_type: practiced · trained · secondary · hobby</code>. The payroll answer stays
unambiguous (<code>is_primary</code>, Tätigkeitsschlüssel/KldB); the agent still sees the whole human.</p></div>
<div class="sf-feat"><div class="sf-ico">🧩</div><h3>Hobby skills count</h3>
<p>Skills attach to the person, not the diploma. Self-assessed abilities flow in with provenance
(<code>self · supervisor · imported · skillfactor</code>) — hidden abilities get a structured data
source, not just observation.</p></div>
<div class="sf-feat"><div class="sf-ico">🎯</div><h3>Right person, right slot</h3>
<p><code>skill_requirements</code> per project, team or shift; the matching engine
(<code>find_matching_employees</code>) returns who qualifies, with match percentage.</p></div>
</div>
<div class="sf-int-grid">
<div class="sf-int-person">
<div class="sf-int-name">Mustermann Max</div>
<div class="sf-int-role">Industriemechaniker · <code>practiced</code> · primary · KldB 24522</div>
<div class="sf-int-role">Fotograf · <code>hobby</code> · self-declared</div>
<div class="sf-int-loads">agent loads ↓</div>
<div class="sf-int-chips">
<a class="sf-int-chip sf-int-chip-main" href="{{AppSubUrl}}/skills-core/industrial-machinery-mechanic">skills-core/industrial-machinery-mechanic <small>main profile</small></a>
<a class="sf-int-chip" href="{{AppSubUrl}}/skills-core/photographer">skills-core/photographer <small>supplementary</small></a>
</div>
</div>
<div class="sf-code-card" style="position:static;">
<div class="sf-code-label">GET /employees/1017/professions</div>
<pre>[
{ "profession": "Industriemechaniker", "slug": "industrial-machinery-mechanic",
"relation_type": <b>"practiced"</b>, "is_primary": true, "kldb_code": "24522" },
{ "profession": "Fotograf", "slug": "photographer",
"relation_type": <b>"hobby"</b>, "is_primary": false, "source": "self-declared" }
]</pre>
<div style="margin-top:12px;font-size:12.5px;color:#8a8a99;">One call — the agent&#39;s full package
list. Slugs resolve to <i>skills-core/{slug}</i>.</div>
</div>
</div>
<p class="sf-center" style="margin-top:22px;font-size:13.5px;color:#8a8a99;">Mapped via ESCO URI · O*NET-SOC · KldB 2010 (Tätigkeitsschlüssel digits 15) — no proprietary IDs.</p>
<p class="sf-center" style="margin-top:10px;"><a class="sf-tlink" href="TODO:/hr_api_data_model_explorer.html?de=1&amp;vendor=skillfactor">Explore the HR data model&nbsp;→</a></p>
</div>
</section>
<!-- Layer model -->
<section class="sf-section">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">Safe sharing</span>
<h2 class="sf-h2">Private stays private. Public knowledge grows.</h2>
</div>
<div class="sf-layers-grid">
<div>
<div class="sf-layer" style="background:#ff355e;"><b>Community</b><span>public profession profiles — anonymized, curated, PR-based intake</span></div>
<div class="sf-layer" style="background:#b30d32;"><b>Organisation</b><span>your company&#39;s standards &amp; processes — private repo, mirrors the community structure</span></div>
<div class="sf-layer" style="background:#800622;"><b>Project</b><span>customer &amp; project lessons — private, access per project folder</span></div>
</div>
<div class="sf-gate">
<h3>Knowledge rises by proving itself</h3>
<ul>
<li>Same insight in ≥ 2 projects → suggested promotion into the org overlay</li>
<li>Proven org knowledge → anonymized second distillate as a community PR</li>
<li><b>Hard rule:</b> nothing leaves <code>projects/</code> automatically — the path outward is always an explicitly confirmed, generalized, anonymized distillate</li>
<li>Agents load the cascade Community → Organisation → Project; more specific beats more general</li>
</ul>
<p style="margin:16px 0 0; font-size:14px;"><a class="sf-tlink" href="{{AppSubUrl}}/gitadmin/skillfactor-pipeline/src/branch/quality/reference-standard/ARCHITECTURE.md">Read the full architecture&nbsp;→</a></p>
</div>
</div>
</div>
</section>
<!-- Multi-platform + enrichment paths -->
<section class="sf-section" style="padding-top: 8px;">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">One content, three platforms</span>
<h2 class="sf-h2">Your profession&#39;s skill — in Claude, ChatGPT and Gemini</h2>
<p class="sf-sub">The git repo is the canonical format: plain Markdown, YAML, JSON — no vendor
syntax in the content. Thin adapters build the same knowledge into an Anthropic agent skill,
a Custom-GPT bundle and a Gemini bundle; one MCP gateway handles reading and writing for all three.</p>
</div>
<div class="sf-features" style="grid-template-columns: repeat(3, 1fr);">
<div class="sf-feat"><div class="sf-ico">🤖</div><h3>Claude</h3><p>Agent-skill package per profession: trigger description, contribution behaviour, competence files as lazy references — installable via plugin marketplace.</p></div>
<div class="sf-feat"><div class="sf-ico">💬</div><h3>ChatGPT</h3><p>Custom-GPT bundle: generated system instruction + competence files as knowledge upload; writing goes through an Action onto the same gateway.</p></div>
<div class="sf-feat"><div class="sf-ico">✨</div><h3>Gemini</h3><p>Instruction + grounding files; writing via function calling against the same gateway. One content, three thin adapters.</p></div>
</div>
<div class="sf-features" style="grid-template-columns: repeat(2, 1fr); margin-top: 18px;">
<div class="sf-feat"><div class="sf-ico">🗣️</div><h3>Enrichment path A — chat distillate</h3><p>Solved something real in a conversation? The skill offers to save it: a project distillate into your private org repo, and — if a transferable core exists — an anonymized distillate as a community pull request. You confirm the full text; nothing commits directly to main.</p></div>
<div class="sf-feat"><div class="sf-ico">📥</div><h3>Enrichment path B — M365 mining</h3><p>A connector mirrors mails, Teams chats and meeting transcripts into SQL Server. A batch job extracts lesson candidates (consent-gated, roles instead of names, raw data never leaves the database) and files them as a review queue of pull requests — no auto-merge.</p></div>
</div>
</div>
</section>
<!-- Contribute example: chat walkthrough -->
<section class="sf-section" id="sf-contribute" style="padding-top: 8px;">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">How to feed your knowledge back</span>
<h2 class="sf-h2">One sentence saves the lesson</h2>
<p class="sf-sub">A project manager — SkillFactor skill installed, connector linked — just learned in a
Claude conversation that customer A ignores her slide-deck status reports, but responds within hours
to a three-bullet email. She keeps that experience:</p>
</div>
<div class="sf-chat">
<div class="sf-row sf-row-user">
<div class="sf-bubble sf-bubble-user">
<div class="sf-prompt-copy"><code id="sf-canonical-prompt">Save the key insight from this chat as a lesson learned in my SkillFactor repo.</code>
<button class="sf-copy-btn" onclick="navigator.clipboard.writeText(document.getElementById('sf-canonical-prompt').textContent);this.textContent='✓ copied';">copy</button>
</div>
<div class="sf-prompt-variants">Variants that work in any conversation: <em>&quot;What should we remember from this chat as a team? File it in the repo.&quot;</em> · <em>&quot;Log this as a lesson for my project customer A.&quot;</em></div>
</div>
<div class="sf-avatar sf-avatar-user">You</div>
</div>
<div class="sf-row sf-row-ai">
<div class="sf-avatar sf-avatar-ai">✳</div>
<div class="sf-bubble sf-bubble-ai">
<p>Happy to — I suggest <b>two placements</b>. Please review both full texts; exactly what you confirm gets committed.</p>
<div class="sf-distillate">
<div class="sf-dist-head">1 · Project distillate → your private org repo <span>projects/customer-a/rollout-2026/lessons/</span></div>
<pre># Customer A: status updates as a three-bullet email
**Situation:** Weekly slide-deck status reports to customer A went
unanswered for weeks.
**Insight:** Customer A's project lead reads email on the go and never
opens attachments. A three-bullet email — done / next / blocked — gets a
response within hours.
**Action for this project:** Send Friday status as a three-bullet email;
keep the deck for the monthly review only.</pre>
</div>
<div class="sf-distillate">
<div class="sf-dist-head">2 · Generalized, anonymized distillate → community (pull request)</div>
<pre># Match the report format to how the reader actually reads
**Pattern:** Status reports fail when the format suits the sender, not
the reader — decks go unread by people who live in their inbox.
**Transferable practice:** Ask (or observe) how your counterpart consumes
information, then default to the shortest format that fits it — e.g. a
three-bullet email: done / next / blocked. Keep long formats for
milestones.</pre>
</div>
<p><b>Both, just one, or neither?</b></p>
</div>
</div>
<div class="sf-row sf-row-user">
<div class="sf-bubble sf-bubble-user"><p>both</p></div>
<div class="sf-avatar sf-avatar-user">You</div>
</div>
<div class="sf-row sf-row-ai">
<div class="sf-avatar sf-avatar-ai">✳</div>
<div class="sf-bubble sf-bubble-ai">
<p>Done — both filed as pull requests (never a direct commit):</p>
<p>✅ Org repo: <a class="sf-tlink" href="#sf-contribute">PR&nbsp;#41 · customer-a/rollout-2026 — &quot;Status as three-bullet email&quot;</a><br>
✅ Community: <a class="sf-tlink" href="#sf-contribute">PR&nbsp;#587 · project-manager — &quot;Match the report format to the reader&quot;</a></p>
<p style="color:#8a8a99;font-size:13.5px;">Both await maintainer review. I checked the target folders for similar lessons first — no duplicates found.</p>
</div>
</div>
</div>
<p class="sf-center" style="margin-top:22px;font-size:14px;color:#515164;">Works the same in <b>ChatGPT</b> and <b>Gemini</b> (adapters on the same gateway). Requirements: the profession skill installed + the SkillFactor connector linked — <a class="sf-tlink" href="{{AppSubUrl}}/gitadmin/skillfactor-pipeline/src/branch/quality/reference-standard/templates/org-repo/README.md">setup guide&nbsp;→</a></p>
</div>
</section>
<!-- Pipeline -->
<section class="sf-section" style="padding-top: 20px;">
<div class="sf-wrap">
<div class="sf-center">
<span class="sf-eyebrow">From occupation database to colleague</span>
<h2 class="sf-h2">Refreshed monthly — skills follow the market</h2>
</div>
<div class="sf-steps">
<div class="sf-step"><div class="sf-num">01</div><b>Sources</b><span>ESCO · O*NET · job boards · Wikipedia · literature · open agent-skill libraries · Stack Exchange practitioner Q&amp;A</span></div>
<div class="sf-step"><div class="sf-num">02</div><b>Evidence store</b><span>requirements from real job ads — weighted by frequency, on Microsoft SQL Server</span></div>
<div class="sf-step"><div class="sf-num">03</div><b>Generator</b><span>builds skill packages: tasks · intake questions · quality criteria · evals</span></div>
<div class="sf-step"><div class="sf-num">04</div><b>Marketplace</b><span>git library — publishing via reviewed pull requests</span></div>
<div class="sf-step"><div class="sf-num">05</div><b>Runtime</b><span>every employee agent loads its profession&#39;s profile</span></div>
</div>
<p class="sf-center" style="margin-top:26px;"><a class="sf-tlink" href="{{AppSubUrl}}/skills-core/marketplace">Visit the marketplace with machine-readable index&nbsp;→</a></p>
</div>
</section>
<!-- CTA -->
<section class="sf-section" style="padding-top: 8px;">
<div class="sf-wrap">
<div class="sf-cta-band sf-reveal">
<h2>The role never forgets.</h2>
<p>3,039 professions are ready — from the production-depth recruiter to the ICT architect,
enriched with the best open-source agent skills. Every skill is a repo, every release a reviewed pull request.</p>
<div class="sf-cta-row" style="justify-content:center;">
<a class="sf-btn sf-btn-white" href="{{AppSubUrl}}/skills-core/artificial-intelligence-engineer/src/branch/main/SKILL.md">Open the AI-engineer skill package</a>
<a class="sf-btn" style="box-shadow:inset 0 0 0 1px rgba(255,255,255,.7); background:transparent; color:#fff !important;" href="{{AppSubUrl}}/explore/repos">All repositories</a>
</div>
</div>
</div>
</section>
<div class="sf-footnote sf-wrap">
skillfactor. Includes information from the O*NET database (USDOL/ETA, CC BY 4.0)
and ESCO (© European Union) — not endorsed by USDOL/ETA or the European Commission.
</div>
</div>
<script>
// Scroll-Reveals + Count-up (IntersectionObserver; Donut-Segmente animieren via .sf-in)
(function () {
// Reveal-Klassen automatisch verteilen: Karten/Steps/Stats einzeln (Stagger via CSS),
// Layer gleiten von links, Gate von rechts, Sektions-Header weich nach oben.
document.querySelectorAll(
".sf-formula .sf-fcard, .sf-features .sf-feat, .sf-steps .sf-step, .sf-stats .sf-stat"
).forEach(function (el) { el.classList.add("sf-reveal"); });
document.querySelectorAll(".sf-layers-grid .sf-layer").forEach(function (el) { el.classList.add("sf-reveal-l"); });
document.querySelectorAll(".sf-layers-grid .sf-gate").forEach(function (el) { el.classList.add("sf-reveal-r"); });
document.querySelectorAll(".sf-center .sf-eyebrow, .sf-center .sf-h2, .sf-center .sf-sub").forEach(function (el) { el.classList.add("sf-reveal"); });
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
e.target.classList.add("sf-in");
e.target.querySelectorAll("[data-count]").forEach(function (el) {
if (el.dataset.done) return;
el.dataset.done = "1";
var target = parseInt(el.dataset.count, 10), t0 = null;
function step(ts) {
if (!t0) t0 = ts;
var p = Math.min((ts - t0) / 1400, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased).toLocaleString("en-US");
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
io.unobserve(e.target);
});
}, { threshold: 0.25 });
document.querySelectorAll(".sf-reveal, .sf-reveal-l, .sf-reveal-r, .sf-reveal-z").forEach(function (el) { io.observe(el); });
// Live-Verarbeitungsstatus: assets/status.json alle 5 min pollen (Cache-Buster)
var statusBox = document.getElementById("sf-live-status");
if (statusBox && window.fetch) {
var setSt = function (k, v) {
statusBox.querySelectorAll('[data-st="' + k + '"]').forEach(function (el) { el.textContent = v; });
};
var loadStatus = function () {
fetch("{{AppSubUrl}}/assets/status.json?t=" + Date.now())
.then(function (r) { return r.json(); })
.then(function (s) {
setSt("done", Number(s.done).toLocaleString("en-US"));
setSt("total", Number(s.total).toLocaleString("en-US"));
setSt("percent", s.percent);
setSt("started", s.started_at);
setSt("elapsed", s.elapsed_h + " h " + s.elapsed_m + " min");
setSt("ollama", Number(s.ollama_requests || 0).toLocaleString("en-US"));
if (s.eta_date) {
var etaEl = document.getElementById("sf-eta-card");
if (etaEl) etaEl.style.display = "";
setSt("eta_date", s.eta_date);
setSt("eta_days", s.eta_days);
}
if (s.se_step) {
var seEl = document.getElementById("sf-se-status");
if (seEl) seEl.style.display = "";
var d = {};
try { d = JSON.parse(s.se_detail || "{}"); } catch (e) {}
// Pipeline-Schritte in Klartext übersetzen
var SE = {
"ingest": { label: "knowledge layer: reading the dumps",
text: "Importing questions and answers from six professional Stack Exchange communities (CC-BY-SA)." },
"ingest-done": { label: "knowledge layer: dumps imported",
text: "All six community archives are in — quality filtering is next." },
"filter-done": { label: "knowledge layer: quality-filtered",
text: "Only highly-voted questions with a proven answer survive the filter." },
"tags-done": { label: "knowledge layer: topics extracted",
num: d.tags_total,
text: "Topic tags collected — they get mapped to official skill taxonomies next." },
"map-done": { label: "knowledge layer: topics mapped",
num: d.mapped,
text: "Community topics matched to ESCO skills and O*NET occupations, each with a confidence grade." },
"compile": { label: "knowledge layer: compiling professions",
text: "Condensing the best practitioner Q&A into per-profession knowledge files — every entry attributed to its author." },
"compile-done": { label: "knowledge layer: professions compiled",
num: d.entries_total,
text: "Curated practitioner insights, grouped by theme, live in the profession packages." },
"competences": { label: "knowledge layer: writing competences",
text: "Generating a practice-focused explanation for every official competence of each profession." },
"competences-done": { label: "knowledge layer: competences complete",
num: (d["model-knowledge"] || 0) + (d["stackexchange"] || 0),
text: "Every competence has a filled explanation — " + (d["stackexchange"] || 0)
+ " grounded in real practitioner Q&A, " + (d["model-knowledge"] || 0)
+ " AI-written from the official ESCO definitions. Provenance is labeled per file." }
};
var info = SE[s.se_step] || { label: "knowledge layer: " + s.se_step, text: "" };
var num = info.num;
if (num == null) { // größte Zahl aus dem Detail als Fallback
num = 0;
for (var k in d) { if (typeof d[k] === "number" && d[k] > num) num = d[k]; }
}
setSt("se_num", num ? Number(num).toLocaleString("en-US") : "✓");
setSt("se_label", info.label);
if (info.text) setSt("se_text", info.text);
}
setSt("updated", s.updated_at);
}).catch(function () {});
};
loadStatus();
setInterval(loadStatus, 300000);
}
// Alle Zahlen aus stats.json (Single Source of Truth, Phase 2c) — die
// eingebetteten data-count-Werte sind nur der Fallback.
if (window.fetch) {
fetch("{{AppSubUrl}}/assets/stats.json?t=" + Date.now())
.then(function (r) { return r.json(); })
.then(function (st) {
document.querySelectorAll("[data-stat]").forEach(function (el) {
var k = el.getAttribute("data-stat");
var v = (k === "flagship_ai_skills") ? (st.flagship || {}).ai_skills_total : st[k];
if (v == null) return;
var txt = Number(v).toLocaleString("en-US");
if (el.hasAttribute("data-count")) {
el.setAttribute("data-count", v);
if (el.dataset.done) el.textContent = txt;
} else {
el.textContent = txt;
}
});
}).catch(function () {});
}
// Donut live aus dem Repo-Manifest speisen (Fallback: eingebettete Werte)
var prov = document.getElementById("sf-prov");
if (prov && window.fetch) {
fetch(prov.dataset.manifest + "?t=" + Date.now()).then(function (r) { return r.json(); }).then(function (m) {
var p = m.provenance || {};
var share = p.share_percent || {}, items = p.items || {};
var order = ["jobads", "onet", "wiki_ai", "esco", "ai_skills", "stackx"], off = 0, total = 0;
order.forEach(function (cat) {
var v = Number(share[cat] || 0);
var c = prov.querySelector('circle[data-cat="' + cat + '"]');
if (c) {
c.style.setProperty("--seg", v + " " + (100 - v));
c.setAttribute("stroke-dashoffset", String(-off));
}
var pct = prov.querySelector('[data-pct="' + cat + '"]');
if (pct) pct.textContent = Math.round(v) + "%";
off += v;
total += Number(items[cat] || 0);
});
var t = prov.querySelector("[data-prov-total]");
if (t && total) t.textContent = total.toLocaleString("en-US");
}).catch(function () { /* Fallback-Werte bleiben stehen */ });
}
})();
</script>
{{template "base/footer" .}}