Files
xsl-validator/web/index_v2.html
T
2026-03-03 20:47:56 +01:00

1475 lines
46 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DocuMentor — XSL-Transformationen unter Kontrolle</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
<style>
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--black: #000000;
--white: #ffffff;
--off-white: #f0f0f0;
--neon-red: #ff0033;
--neon-red-dim: #cc0029;
--gray-dark: #1a1a1a;
--gray-mid: #333333;
--gray-light: #888888;
--border-thick: 4px;
--border-thin: 2px;
--font-mono: 'Space Mono', 'Courier New', monospace;
}
html {
scroll-behavior: auto;
}
body {
font-family: var(--font-mono);
background: var(--white);
color: var(--black);
line-height: 1.6;
overflow-x: hidden;
}
/* ========== BRUTALIST RESETS ========== */
::selection {
background: var(--neon-red);
color: var(--white);
}
a {
color: var(--neon-red);
text-decoration: none;
border-bottom: var(--border-thin) solid var(--neon-red);
}
a:hover {
background: var(--neon-red);
color: var(--white);
}
/* ========== GRID OVERLAY ========== */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px),
linear-gradient(0deg, rgba(0,0,0,0.03) 1px, transparent 1px);
background-size: 60px 60px;
pointer-events: none;
z-index: 0;
}
/* ========== NAVIGATION ========== */
nav {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
background: var(--black);
color: var(--white);
border-bottom: var(--border-thick) solid var(--neon-red);
padding: 0;
}
nav .nav-inner {
display: flex;
align-items: stretch;
justify-content: space-between;
max-width: 1400px;
margin: 0 auto;
}
nav .nav-brand {
font-weight: 700;
font-size: 1.1rem;
text-transform: uppercase;
letter-spacing: 3px;
padding: 14px 24px;
border-right: var(--border-thin) solid var(--gray-mid);
color: var(--white);
border-bottom: none;
display: flex;
align-items: center;
gap: 8px;
}
nav .nav-brand span {
color: var(--neon-red);
}
nav .nav-links {
display: flex;
list-style: none;
align-items: stretch;
}
nav .nav-links li {
border-left: var(--border-thin) solid var(--gray-mid);
}
nav .nav-links a {
display: flex;
align-items: center;
padding: 14px 20px;
color: var(--white);
text-transform: uppercase;
font-size: 0.7rem;
letter-spacing: 2px;
border-bottom: none;
transition: none;
}
nav .nav-links a:hover {
background: var(--neon-red);
color: var(--white);
}
/* ========== HERO ========== */
.hero {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
padding: 120px 40px 80px;
background: var(--black);
color: var(--white);
border-bottom: var(--border-thick) solid var(--neon-red);
overflow: hidden;
}
.hero::before {
content: 'XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF XSL→PDF';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 100%;
font-size: 1.2rem;
color: rgba(255, 255, 255, 0.03);
word-break: break-all;
line-height: 2;
letter-spacing: 8px;
pointer-events: none;
z-index: 0;
}
.hero-content {
position: relative;
z-index: 1;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.hero-label {
display: inline-block;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 4px;
border: var(--border-thin) solid var(--neon-red);
padding: 6px 16px;
margin-bottom: 40px;
color: var(--neon-red);
}
.hero h1 {
font-family: var(--font-mono);
font-size: clamp(3rem, 10vw, 8rem);
font-weight: 700;
text-transform: uppercase;
line-height: 0.95;
letter-spacing: -3px;
margin-bottom: 40px;
}
.hero h1 .red {
color: var(--neon-red);
display: block;
}
.hero-tagline {
font-size: 1.1rem;
max-width: 700px;
line-height: 1.8;
color: var(--gray-light);
border-left: var(--border-thick) solid var(--neon-red);
padding-left: 24px;
margin-bottom: 50px;
}
.hero-stats {
display: flex;
gap: 0;
border: var(--border-thick) solid var(--white);
width: fit-content;
}
.hero-stat {
padding: 20px 32px;
border-right: var(--border-thin) solid var(--gray-mid);
text-align: center;
}
.hero-stat:last-child {
border-right: none;
}
.hero-stat .number {
font-size: 2rem;
font-weight: 700;
color: var(--neon-red);
display: block;
}
.hero-stat .label {
font-size: 0.6rem;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--gray-light);
}
/* ========== SECTION BASE ========== */
section {
position: relative;
z-index: 1;
padding: 100px 40px;
}
.section-inner {
max-width: 1400px;
margin: 0 auto;
}
.section-header {
margin-bottom: 60px;
}
.section-number {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 4px;
color: var(--neon-red);
margin-bottom: 12px;
display: block;
}
.section-title {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 700;
text-transform: uppercase;
letter-spacing: -1px;
line-height: 1.1;
}
.section-title .muted {
color: var(--gray-light);
}
.section-divider {
width: 100%;
height: var(--border-thick);
background: var(--black);
margin-top: 20px;
}
/* ========== FEATURES ========== */
.features {
background: var(--white);
border-bottom: var(--border-thick) solid var(--black);
}
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
border: var(--border-thick) solid var(--black);
}
.feature-card {
padding: 40px 32px;
border-right: var(--border-thin) solid var(--black);
border-bottom: var(--border-thin) solid var(--black);
position: relative;
transition: none;
}
.feature-card:nth-child(3n) {
border-right: none;
}
.feature-card:nth-child(n+4) {
border-bottom: none;
}
.feature-card:hover {
background: var(--black);
color: var(--white);
}
.feature-card:hover .feature-icon {
color: var(--neon-red);
}
.feature-card:hover .feature-num {
color: var(--neon-red);
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 16px;
display: block;
filter: grayscale(0);
}
.feature-num {
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 3px;
color: var(--gray-light);
margin-bottom: 12px;
display: block;
}
.feature-card h3 {
font-size: 1.1rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
font-weight: 700;
}
.feature-card p {
font-size: 0.8rem;
line-height: 1.7;
color: var(--gray-light);
}
.feature-card:hover p {
color: var(--gray-light);
}
/* ========== WORKFLOW ========== */
.workflow {
background: var(--black);
color: var(--white);
border-bottom: var(--border-thick) solid var(--neon-red);
}
.workflow .section-title {
color: var(--white);
}
.workflow .section-divider {
background: var(--white);
}
.workflow-steps {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0;
border: var(--border-thick) solid var(--white);
}
.workflow-step {
padding: 40px 28px;
border-right: var(--border-thin) solid var(--gray-mid);
position: relative;
}
.workflow-step:last-child {
border-right: none;
}
.step-num {
font-size: 4rem;
font-weight: 700;
color: var(--neon-red);
line-height: 1;
margin-bottom: 20px;
display: block;
}
.workflow-step h3 {
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
font-weight: 700;
}
.workflow-step p {
font-size: 0.78rem;
line-height: 1.7;
color: var(--gray-light);
}
.workflow-step .arrow {
position: absolute;
right: -14px;
top: 50%;
transform: translateY(-50%);
font-size: 1.5rem;
color: var(--neon-red);
z-index: 2;
}
/* ========== DEMO / SCREENSHOT ========== */
.demo {
background: var(--off-white);
border-bottom: var(--border-thick) solid var(--black);
}
.demo-mockup {
border: var(--border-thick) solid var(--black);
background: var(--white);
position: relative;
}
.demo-titlebar {
background: var(--black);
color: var(--white);
padding: 10px 20px;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 2px;
}
.demo-dots {
display: flex;
gap: 8px;
}
.demo-dot {
width: 12px;
height: 12px;
border: var(--border-thin) solid var(--white);
}
.demo-dot.red {
background: var(--neon-red);
border-color: var(--neon-red);
}
.demo-body {
display: grid;
grid-template-columns: 260px 1fr;
min-height: 500px;
}
.demo-sidebar {
border-right: var(--border-thick) solid var(--black);
padding: 20px;
font-size: 0.72rem;
background: var(--off-white);
}
.demo-tree-item {
padding: 6px 0;
padding-left: 0;
border-bottom: 1px solid #ddd;
}
.demo-tree-item.indent-1 { padding-left: 16px; }
.demo-tree-item.indent-2 { padding-left: 32px; }
.demo-tree-item.indent-3 { padding-left: 48px; }
.demo-tree-item .icon {
margin-right: 6px;
}
.demo-tree-item.active {
background: var(--neon-red);
color: var(--white);
padding: 6px 8px;
margin: 0 -20px;
padding-left: 52px;
}
.demo-main {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.demo-panel {
border-right: var(--border-thin) solid var(--black);
position: relative;
display: flex;
flex-direction: column;
}
.demo-panel:last-child {
border-right: none;
}
.demo-panel-header {
padding: 10px 16px;
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 2px;
border-bottom: var(--border-thin) solid var(--black);
background: var(--off-white);
font-weight: 700;
}
.demo-panel-body {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 30px;
position: relative;
min-height: 400px;
}
.demo-pdf-placeholder {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 0.7rem;
color: var(--gray-light);
text-transform: uppercase;
letter-spacing: 2px;
}
.demo-pdf-placeholder .pdf-icon {
font-size: 3rem;
margin-bottom: 12px;
opacity: 0.4;
}
.demo-panel.diff .demo-panel-header {
background: var(--neon-red);
color: var(--white);
}
.demo-panel.diff .demo-pdf-placeholder {
background: repeating-linear-gradient(
45deg,
transparent,
transparent 10px,
rgba(255, 0, 51, 0.04) 10px,
rgba(255, 0, 51, 0.04) 20px
);
}
.demo-statusbar {
background: var(--black);
color: var(--gray-light);
padding: 8px 20px;
font-size: 0.65rem;
display: flex;
justify-content: space-between;
text-transform: uppercase;
letter-spacing: 1px;
}
.demo-statusbar .status-ok {
color: #00ff66;
}
/* ========== TECH STACK ========== */
.tech {
background: var(--white);
border-bottom: var(--border-thick) solid var(--black);
}
.tech-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
border: var(--border-thick) solid var(--black);
}
.tech-item {
padding: 32px 24px;
border-right: var(--border-thin) solid var(--black);
border-bottom: var(--border-thin) solid var(--black);
text-align: center;
}
.tech-item:nth-child(4n) {
border-right: none;
}
.tech-item:nth-child(n+5) {
border-bottom: none;
}
.tech-item:hover {
background: var(--black);
color: var(--white);
}
.tech-item .tech-icon {
font-size: 2rem;
margin-bottom: 10px;
display: block;
}
.tech-item h4 {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 2px;
font-weight: 700;
margin-bottom: 6px;
}
.tech-item p {
font-size: 0.68rem;
color: var(--gray-light);
letter-spacing: 1px;
}
.tech-item:hover p {
color: var(--gray-light);
}
/* ========== DOWNLOAD ========== */
.download {
background: var(--black);
color: var(--white);
border-bottom: var(--border-thick) solid var(--neon-red);
}
.download .section-title {
color: var(--white);
}
.download .section-divider {
background: var(--white);
}
.download-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
border: var(--border-thick) solid var(--white);
}
.download-info {
padding: 48px 40px;
border-right: var(--border-thick) solid var(--white);
}
.download-info h3 {
font-size: 1.2rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 20px;
font-weight: 700;
}
.download-info p {
font-size: 0.82rem;
line-height: 1.8;
color: var(--gray-light);
margin-bottom: 24px;
}
.download-info ul {
list-style: none;
margin-bottom: 32px;
}
.download-info ul li {
padding: 8px 0;
font-size: 0.78rem;
border-bottom: 1px solid var(--gray-mid);
display: flex;
align-items: center;
gap: 10px;
}
.download-info ul li .check {
color: var(--neon-red);
font-weight: 700;
}
.download-code {
padding: 48px 40px;
display: flex;
flex-direction: column;
justify-content: center;
}
.code-block {
background: var(--gray-dark);
border: var(--border-thin) solid var(--gray-mid);
padding: 0;
margin-bottom: 20px;
}
.code-block-header {
padding: 10px 16px;
font-size: 0.6rem;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--gray-light);
border-bottom: var(--border-thin) solid var(--gray-mid);
}
.code-block pre {
padding: 20px;
font-family: var(--font-mono);
font-size: 0.82rem;
line-height: 1.8;
overflow-x: auto;
color: var(--off-white);
}
.code-block pre .comment {
color: var(--gray-light);
}
.code-block pre .cmd {
color: var(--neon-red);
}
.btn-brutal {
display: inline-block;
padding: 16px 32px;
background: var(--neon-red);
color: var(--white);
font-family: var(--font-mono);
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 3px;
border: var(--border-thick) solid var(--white);
cursor: pointer;
border-bottom: var(--border-thick) solid var(--white);
text-align: center;
}
.btn-brutal:hover {
background: var(--white);
color: var(--black);
}
/* ========== FOOTER ========== */
footer {
background: var(--black);
color: var(--white);
padding: 0;
}
.footer-inner {
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
border-top: var(--border-thick) solid var(--neon-red);
}
.footer-col {
padding: 40px 32px;
border-right: var(--border-thin) solid var(--gray-mid);
}
.footer-col:last-child {
border-right: none;
}
.footer-col h4 {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 3px;
margin-bottom: 20px;
color: var(--neon-red);
font-weight: 700;
}
.footer-col p,
.footer-col li {
font-size: 0.72rem;
line-height: 2;
color: var(--gray-light);
}
.footer-col ul {
list-style: none;
}
.footer-col a {
color: var(--gray-light);
border-bottom: 1px solid var(--gray-mid);
}
.footer-col a:hover {
color: var(--white);
background: var(--neon-red);
}
.footer-bottom {
border-top: var(--border-thin) solid var(--gray-mid);
padding: 20px 40px;
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--gray-light);
display: flex;
justify-content: space-between;
max-width: 1400px;
margin: 0 auto;
}
/* ========== MARQUEE ========== */
.marquee-bar {
background: var(--neon-red);
color: var(--white);
padding: 10px 0;
overflow: hidden;
white-space: nowrap;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 4px;
font-weight: 700;
border-bottom: var(--border-thick) solid var(--black);
}
.marquee-content {
display: inline-block;
animation: marquee 20s linear infinite;
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
/* ========== GLITCH EFFECT ========== */
.glitch {
position: relative;
}
.glitch:hover::before,
.glitch:hover::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.glitch:hover::before {
color: var(--neon-red);
z-index: -1;
animation: glitch-1 0.3s steps(2) infinite;
}
.glitch:hover::after {
color: cyan;
z-index: -2;
animation: glitch-2 0.3s steps(2) infinite;
}
@keyframes glitch-1 {
0% { clip-path: inset(20% 0 60% 0); transform: translate(-3px, 0); }
50% { clip-path: inset(50% 0 20% 0); transform: translate(3px, 0); }
100% { clip-path: inset(10% 0 70% 0); transform: translate(-2px, 0); }
}
@keyframes glitch-2 {
0% { clip-path: inset(60% 0 10% 0); transform: translate(3px, 0); }
50% { clip-path: inset(20% 0 50% 0); transform: translate(-3px, 0); }
100% { clip-path: inset(70% 0 5% 0); transform: translate(2px, 0); }
}
/* ========== RESPONSIVE ========== */
@media (max-width: 1024px) {
.features-grid {
grid-template-columns: repeat(2, 1fr);
}
.feature-card:nth-child(3n) {
border-right: var(--border-thin) solid var(--black);
}
.feature-card:nth-child(2n) {
border-right: none;
}
.feature-card:nth-child(n+4) {
border-bottom: var(--border-thin) solid var(--black);
}
.feature-card:nth-child(n+5) {
border-bottom: none;
}
.workflow-steps {
grid-template-columns: repeat(2, 1fr);
}
.workflow-step:nth-child(2) .arrow {
display: none;
}
.workflow-step {
border-bottom: var(--border-thin) solid var(--gray-mid);
}
.tech-grid {
grid-template-columns: repeat(2, 1fr);
}
.download-content {
grid-template-columns: 1fr;
}
.download-info {
border-right: none;
border-bottom: var(--border-thick) solid var(--white);
}
.demo-body {
grid-template-columns: 1fr;
}
.demo-sidebar {
border-right: none;
border-bottom: var(--border-thick) solid var(--black);
}
.demo-main {
grid-template-columns: 1fr;
}
.demo-panel {
border-right: none;
border-bottom: var(--border-thin) solid var(--black);
}
.demo-panel-body {
min-height: 200px;
}
}
@media (max-width: 768px) {
section {
padding: 60px 20px;
}
.hero {
padding: 100px 20px 60px;
}
nav .nav-links {
display: none;
}
.features-grid {
grid-template-columns: 1fr;
}
.feature-card {
border-right: none !important;
border-bottom: var(--border-thin) solid var(--black) !important;
}
.feature-card:last-child {
border-bottom: none !important;
}
.workflow-steps {
grid-template-columns: 1fr;
}
.workflow-step {
border-right: none;
}
.workflow-step .arrow {
display: none;
}
.tech-grid {
grid-template-columns: 1fr;
}
.tech-item {
border-right: none !important;
}
.footer-inner {
grid-template-columns: 1fr;
}
.footer-col {
border-right: none;
border-bottom: var(--border-thin) solid var(--gray-mid);
}
.hero-stats {
flex-direction: column;
}
.hero-stat {
border-right: none;
border-bottom: var(--border-thin) solid var(--gray-mid);
}
.hero-stat:last-child {
border-bottom: none;
}
}
/* ========== BLINK CURSOR ========== */
.cursor {
display: inline-block;
width: 3px;
height: 1em;
background: var(--neon-red);
margin-left: 4px;
vertical-align: text-bottom;
animation: blink 0.7s step-end infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* ========== SCROLL REVEAL ========== */
.reveal {
opacity: 0;
transform: translateY(30px);
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
transition: opacity 0.1s step-end, transform 0.1s step-end;
}
</style>
</head>
<body>
<!-- NAVIGATION -->
<nav>
<div class="nav-inner">
<a href="#hero" class="nav-brand"><span>&#9632;</span> DocuMentor</a>
<ul class="nav-links">
<li><a href="#features">Features</a></li>
<li><a href="#workflow">Workflow</a></li>
<li><a href="#demo">Demo</a></li>
<li><a href="#tech">Tech-Stack</a></li>
<li><a href="#download">Download</a></li>
</ul>
</div>
</nav>
<!-- HERO -->
<section class="hero" id="hero">
<div class="hero-content">
<span class="hero-label">Desktop-Anwendung // PySide6 // Open Source</span>
<h1>
<span class="glitch" data-text="DOCU">DOCU</span><span class="red glitch" data-text="MENTOR">MENTOR</span>
</h1>
<p class="hero-tagline">
XSL-Transformationen unter Kontrolle. Validieren. Vergleichen. Verifizieren.
Die Toolchain für Entwickler, die PDF-Dokumente aus XSL/XML erzeugen
und Änderungen lückenlos nachverfolgen muessen.<span class="cursor"></span>
</p>
<div class="hero-stats">
<div class="hero-stat">
<span class="number">100+</span>
<span class="label">XSL-Dateien</span>
</div>
<div class="hero-stat">
<span class="number">3</span>
<span class="label">PDF-Panels</span>
</div>
<div class="hero-stat">
<span class="number">BLAKE2B</span>
<span class="label">Hash-Tracking</span>
</div>
<div class="hero-stat">
<span class="number">&infin;</span>
<span class="label">Transformationen</span>
</div>
</div>
</div>
</section>
<!-- MARQUEE -->
<div class="marquee-bar">
<div class="marquee-content">
SAXON &bull; APACHE FOP &bull; DIFF-PDF &bull; POSTGRESQL &bull; PYSIDE6 &bull; BLAKE2B &bull; XSL &bull; XML &bull; PDF &bull;
SAXON &bull; APACHE FOP &bull; DIFF-PDF &bull; POSTGRESQL &bull; PYSIDE6 &bull; BLAKE2B &bull; XSL &bull; XML &bull; PDF &bull;
SAXON &bull; APACHE FOP &bull; DIFF-PDF &bull; POSTGRESQL &bull; PYSIDE6 &bull; BLAKE2B &bull; XSL &bull; XML &bull; PDF &bull;
SAXON &bull; APACHE FOP &bull; DIFF-PDF &bull; POSTGRESQL &bull; PYSIDE6 &bull; BLAKE2B &bull; XSL &bull; XML &bull; PDF &bull;
</div>
</div>
<!-- FEATURES -->
<section class="features" id="features">
<div class="section-inner">
<div class="section-header reveal">
<span class="section-number">01 // Features</span>
<h2 class="section-title">Was DocuMentor <span class="muted">leistet</span></h2>
<div class="section-divider"></div>
</div>
<div class="features-grid reveal">
<div class="feature-card">
<span class="feature-icon">&#9776;</span>
<span class="feature-num">Feature 01</span>
<h3>Baumstruktur</h3>
<p>Hierarchische Organisation von XSL-Transformationen. TreeNode, XslFile und XmlFile bilden eine übersichtliche Projektstruktur. Import- und Include-Abhängigkeiten auf einen Blick.</p>
</div>
<div class="feature-card">
<span class="feature-icon">&#9638;</span>
<span class="feature-num">Feature 02</span>
<h3>PDF-Diff</h3>
<p>Drei-Panel-Ansicht: Referenz, Diff und Neu. Visueller Vergleich mit Alpha-Blending. Änderungen in PDF-Dokumenten sofort erkennen — Pixel für Pixel.</p>
</div>
<div class="feature-card">
<span class="feature-icon">#_</span>
<span class="feature-num">Feature 03</span>
<h3>Hash-Tracking</h3>
<p>BLAKE2B-Hashing zur Änderungsverfolgung von XML-Dateien. Automatische Berechnung beim Laden. 64-Zeichen-Hexdigest in project.yaml persistiert.</p>
</div>
<div class="feature-card">
<span class="feature-icon">&#8644;</span>
<span class="feature-num">Feature 04</span>
<h3>Async-Verarbeitung</h3>
<p>Hintergrund-Threads für Hash-Berechnungen und Datenbank-Abfragen. Die UI bleibt reaktionsfähig — auch bei 100+ Dateien. Abbrechen-Dialog inklusive.</p>
</div>
<div class="feature-card">
<span class="feature-icon">&#9107;</span>
<span class="feature-num">Feature 05</span>
<h3>PostgreSQL</h3>
<p>Datenbankintegration mit Polars und ConnectorX. SSL-Unterstützung. SQL-Abfragen direkt aus der Anwendung. Ergebnisse als DataFrames.</p>
</div>
<div class="feature-card">
<span class="feature-icon">&#9881;</span>
<span class="feature-num">Feature 06</span>
<h3>Toolchain-Konfig</h3>
<p>Zentrale Verwaltung: Java VM, Saxon JAR, Apache FOP, diff-pdf. ID-basierte Referenzierung. Projekte koennen verschiedene Tool-Versionen verwenden.</p>
</div>
</div>
</div>
</section>
<!-- WORKFLOW -->
<section class="workflow" id="workflow">
<div class="section-inner">
<div class="section-header reveal">
<span class="section-number">02 // Workflow</span>
<h2 class="section-title">Vier Schritte <span style="color:var(--neon-red);">zum Ergebnis</span></h2>
<div class="section-divider"></div>
</div>
<div class="workflow-steps reveal">
<div class="workflow-step">
<span class="step-num">01</span>
<h3>XSL bearbeiten</h3>
<p>Änderungen an den XSL-Dateien durchführen. Import- und Include-Verknüpfungen beachten — eine Änderung kann viele Dokumente betreffen.</p>
<span class="arrow">&#9654;</span>
</div>
<div class="workflow-step">
<span class="step-num">02</span>
<h3>Transformation starten</h3>
<p>Saxon führt die XSLT-Transformation durch. Apache FOP erzeugt die PDF-Ausgabe. Alles konfigurierbar über die Toolchain.</p>
<span class="arrow">&#9654;</span>
</div>
<div class="workflow-step">
<span class="step-num">03</span>
<h3>PDF-Diff begutachten</h3>
<p>Die Drei-Panel-Ansicht zeigt Referenz, Differenz und neues Dokument. diff-pdf markiert alle Änderungen visuell.</p>
<span class="arrow">&#9654;</span>
</div>
<div class="workflow-step">
<span class="step-num">04</span>
<h3>Ergebnis verifizieren</h3>
<p>Wurden die richtigen PDFs geändert? Entsprechen die Änderungen den Erwartungen? Bei Bedarf: zurück zu Schritt 1.</p>
</div>
</div>
</div>
</section>
<!-- DEMO / SCREENSHOT -->
<section class="demo" id="demo">
<div class="section-inner">
<div class="section-header reveal">
<span class="section-number">03 // Oberfläche</span>
<h2 class="section-title">Die Anwendung <span class="muted">im Einsatz</span></h2>
<div class="section-divider"></div>
</div>
<div class="demo-mockup reveal">
<div class="demo-titlebar">
<div class="demo-dots">
<div class="demo-dot red"></div>
<div class="demo-dot"></div>
<div class="demo-dot"></div>
</div>
<span>DocuMentor v1.0 — Projekt: Pruefungsdokumente</span>
<span style="color:var(--gray-light);">&#9632; PySide6</span>
</div>
<div class="demo-body">
<div class="demo-sidebar">
<div class="demo-tree-item"><span class="icon">&#9660;</span> <strong>Urkunden</strong></div>
<div class="demo-tree-item indent-1"><span class="icon">&#9660;</span> bachelor_urkunde.xsl</div>
<div class="demo-tree-item indent-2"><span class="icon">&#9679;</span> student_01.xml</div>
<div class="demo-tree-item indent-2 active"><span class="icon">&#9679;</span> student_02.xml</div>
<div class="demo-tree-item indent-2"><span class="icon">&#9679;</span> student_03.xml</div>
<div class="demo-tree-item indent-1"><span class="icon">&#9654;</span> master_urkunde.xsl</div>
<div class="demo-tree-item"><span class="icon">&#9660;</span> <strong>Zeugnisse</strong></div>
<div class="demo-tree-item indent-1"><span class="icon">&#9654;</span> zeugnis_ba.xsl</div>
<div class="demo-tree-item indent-1"><span class="icon">&#9654;</span> zeugnis_ma.xsl</div>
<div class="demo-tree-item"><span class="icon">&#9654;</span> <strong>Bescheide</strong></div>
</div>
<div class="demo-main">
<div class="demo-panel">
<div class="demo-panel-header">Referenz-PDF</div>
<div class="demo-panel-body">
<div class="demo-pdf-placeholder">
<span class="pdf-icon">&#9724;</span>
Referenz
</div>
</div>
</div>
<div class="demo-panel diff">
<div class="demo-panel-header">&#9632; Diff-Ansicht</div>
<div class="demo-panel-body">
<div class="demo-pdf-placeholder">
<span class="pdf-icon">&#9724;</span>
Differenz
</div>
</div>
</div>
<div class="demo-panel">
<div class="demo-panel-header">Neues PDF</div>
<div class="demo-panel-body">
<div class="demo-pdf-placeholder">
<span class="pdf-icon">&#9724;</span>
Neu generiert
</div>
</div>
</div>
</div>
</div>
<div class="demo-statusbar">
<span>student_02.xml &mdash; BLAKE2B: <span style="color:var(--neon-red);">a3f7c9...</span></span>
<span class="status-ok">&#9632; Transformation abgeschlossen</span>
</div>
</div>
</div>
</section>
<!-- TECH STACK -->
<section class="tech" id="tech">
<div class="section-inner">
<div class="section-header reveal">
<span class="section-number">04 // Tech-Stack</span>
<h2 class="section-title">Gebaut mit <span class="muted">roher Kraft</span></h2>
<div class="section-divider"></div>
</div>
<div class="tech-grid reveal">
<div class="tech-item">
<span class="tech-icon">&#9670;</span>
<h4>Python</h4>
<p>Kern-Sprache</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9635;</span>
<h4>PySide6</h4>
<p>Qt-GUI-Framework</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9830;</span>
<h4>Saxon</h4>
<p>XSLT-Prozessor</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9646;</span>
<h4>Apache FOP</h4>
<p>PDF-Erzeugung</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9644;</span>
<h4>diff-pdf</h4>
<p>PDF-Vergleich</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9107;</span>
<h4>PostgreSQL</h4>
<p>Datenbank</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9783;</span>
<h4>Polars</h4>
<p>DataFrames</p>
</div>
<div class="tech-item">
<span class="tech-icon">&#9881;</span>
<h4>Pydantic</h4>
<p>Konfiguration</p>
</div>
</div>
</div>
</section>
<!-- DOWNLOAD -->
<section class="download" id="download">
<div class="section-inner">
<div class="section-header reveal">
<span class="section-number">05 // Loslegen</span>
<h2 class="section-title">Jetzt <span style="color:var(--neon-red);">installieren</span></h2>
<div class="section-divider"></div>
</div>
<div class="download-content reveal">
<div class="download-info">
<h3>Voraussetzungen</h3>
<p>DocuMentor läuft auf Linux, Windows und macOS. Die Konfiguration wird plattformspezifisch gespeichert.</p>
<ul>
<li><span class="check">[x]</span> Python 3.13+</li>
<li><span class="check">[x]</span> Java VM (für Saxon)</li>
<li><span class="check">[x]</span> Saxon HE/PE/EE JAR</li>
<li><span class="check">[x]</span> Apache FOP</li>
<li><span class="check">[x]</span> diff-pdf</li>
<li><span class="check">[x]</span> PostgreSQL (optional)</li>
</ul>
<a href="#" class="btn-brutal">Dokumentation</a>
</div>
<div class="download-code">
<div class="code-block">
<div class="code-block-header">Terminal</div>
<pre><span class="comment"># Repository klonen</span>
<span class="cmd">$</span> git clone https://github.com/user/xsl-validator.git
<span class="cmd">$</span> cd xsl-validator
<span class="comment"># Abhängigkeiten installieren</span>
<span class="cmd">$</span> uv sync
<span class="comment"># Anwendung starten</span>
<span class="cmd">$</span> uv run python src/main.py</pre>
</div>
<div class="code-block">
<div class="code-block-header">Linting</div>
<pre><span class="cmd">$</span> uv run ruff check
<span class="cmd">$</span> uv run ruff format</pre>
</div>
</div>
</div>
</div>
</section>
<!-- FOOTER -->
<footer>
<div class="footer-inner">
<div class="footer-col">
<h4>DocuMentor</h4>
<p>Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen. Entwickelt für die kontinuierliche Weiterentwicklung von PDF-Dokumenten in Flexnow.</p>
</div>
<div class="footer-col">
<h4>Links</h4>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#workflow">Workflow</a></li>
<li><a href="#demo">Oberfläche</a></li>
<li><a href="#tech">Tech-Stack</a></li>
<li><a href="#download">Download</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Tech</h4>
<ul>
<li>PySide6 / Qt</li>
<li>Saxon XSLT</li>
<li>Apache FOP</li>
<li>diff-pdf</li>
<li>PostgreSQL / Polars</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<span>&copy; 2026 DocuMentor</span>
<span>Gebaut mit roher Kraft.</span>
</div>
</footer>
<script>
// Brutalist scroll reveal — abrupt, no easing
document.addEventListener('DOMContentLoaded', () => {
const reveals = document.querySelectorAll('.reveal');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.15
});
reveals.forEach(el => observer.observe(el));
});
// Nav active state
document.addEventListener('scroll', () => {
const sections = document.querySelectorAll('section[id]');
const links = document.querySelectorAll('.nav-links a');
let current = '';
sections.forEach(section => {
const top = section.offsetTop - 80;
if (window.scrollY >= top) {
current = section.getAttribute('id');
}
});
links.forEach(link => {
link.style.background = '';
link.style.color = '';
if (link.getAttribute('href') === '#' + current) {
link.style.background = 'var(--neon-red)';
link.style.color = 'var(--white)';
}
});
});
</script>
</body>
</html>