PageSourceSearch

https://textwash.eu/cloud/

html textwash.eu collected 2026-09-25 22:35:12 UTC 58,181 bytes, 1,414 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="UTF-8" />
5  <title>Sign in | Textwash™ Pro Cloud</title>
6  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
7  <meta name="theme-color" content="#ffffff" />
8  <meta name="color-scheme" content="light" />
9  <link rel="icon" href="/favicon.ico" sizes="any" />
10  <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
11  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
12  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
13  <link rel="apple-touch-icon" href="/apple-icon-180x180.png" />
14  <link rel="manifest" href="/manifest.json" />
15  <meta name="description" content="Sign in to Textwash™ Pro Cloud, the AI workspace where every prompt is anonymised before it reaches Claude, ChatGPT, DeepSeek or Google Gemini. No account yet? Book a call and we will set one up." />
16  <link rel="canonical" href="https://www.textwash.eu/cloud/" />
17  <link rel="alternate" hreflang="en" href="https://www.textwash.eu/cloud/" />
18  <link rel="alternate" hreflang="de" href="https://www.textwash.eu/de/cloud/" />
19  <link rel="alternate" hreflang="x-default" href="https://www.textwash.eu/cloud/" />
20
21  <!-- Social sharing -->
22  <meta property="og:type" content="website" />
23  <meta property="og:site_name" content="Textwash™ Pro" />
24  <meta property="og:title" content="Sign in | Textwash™ Pro Cloud" />
25  <meta property="og:description" content="Sign in to Textwash™ Pro Cloud, the AI workspace where every prompt is anonymised before it reaches Claude, ChatGPT, DeepSeek or Google Gemini." />
26  <meta property="og:url" content="https://www.textwash.eu/cloud/" />
27  <meta property="og:image" content="https://www.textwash.eu/logo.png" />
28  <meta property="og:locale" content="en" />
29  <meta name="twitter:card" content="summary" />
30  <meta name="twitter:title" content="Sign in | Textwash™ Pro Cloud" />
31  <meta name="twitter:description" content="Sign in to Textwash™ Pro Cloud, the AI workspace where every prompt is anonymised before it reaches Claude, ChatGPT, DeepSeek or Google Gemini." />
32  <meta name="twitter:image" content="https://www.textwash.eu/logo.png" />
33
34  <!-- Inter font (CDN) -->
35  <link rel="preconnect" href="https://fonts.googleapis.com" />
36  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
37  <link rel="preconnect" href="https://www.jocapps.com" crossorigin />
38  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
39<meta name="msvalidate.01" content="82696D54F75F65F1FC0FD72F864B472B" />
40
41  <!-- Production-ready local Tailwind build (used by the dialogs) -->
42  <link rel="stylesheet" href="/styles.css" />
43
44  <!-- Design system -->
45  <style>
46    :root {
47      --ink: #1d1d1f;
48      --ink-2: #46464c;
49      --ink-3: #6e6e73;
50      --bg: #ffffff;
51      --bg-soft: #f5f5f7;
52      --line: #e8e8ed;
53      --brand: #059669;
54      --brand-ink: #047857;
55      --brand-soft: rgba(16, 185, 129, 0.09);
56      --grad: linear-gradient(110deg, #10b981, #0ea5a4 45%, #0284c7);
57      --radius: 22px;
58      --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04), 0 20px 50px rgba(29, 29, 31, 0.07);
59    }
60
61    html { scroll-behavior: smooth; }
62
63    body {
64      margin: 0;
65      font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
66      background: var(--bg);
67      color: var(--ink-2);
68      -webkit-font-smoothing: antialiased;
69      font-size: 17px;
70      line-height: 1.55;
71    }
72
73    ::selection { background: rgba(16, 185, 129, 0.18); color: #0f172a; }
74
75    h1, h2, h3 { color: var(--ink); text-wrap: balance; margin: 0; }
76    /* Spelled out rather than left to the Tailwind reset: line-height: 0 keeps a
77       superscript from stretching the line it sits on, so the leading stays even. */
78    sup { font-size: 0.72em; line-height: 0; position: relative; top: -0.42em; vertical-align: baseline; }
79
80    .wrap { max-width: 1104px; margin: 0 auto; padding: 0 24px; }
81    .wrap-tight { max-width: 880px; margin: 0 auto; padding: 0 24px; }
82
83    .sec { padding: 88px 0; }
84    .sec-soft { background: var(--bg-soft); }
85
86    .eyebrow {
87      display: inline-flex;
88      align-items: center;
89      gap: 8px;
90      font-size: 13px;
91      font-weight: 600;
92      letter-spacing: 0.12em;
93      text-transform: uppercase;
94      color: var(--brand-ink);
95      margin-bottom: 14px;
96    }
97
98    .h-xl {
99      font-size: clamp(2.6rem, 6.4vw, 4.6rem);
100      line-height: 1.04;
101      font-weight: 800;
102      letter-spacing: -0.03em;
103    }
104
105    .h-lg {
106      font-size: clamp(1.9rem, 4vw, 2.9rem);
107      line-height: 1.08;
108      font-weight: 700;
109      letter-spacing: -0.025em;
110    }
111
112    .h-md { font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em; }
113
114    .grad-text {
115      background: var(--grad);
116      -webkit-background-clip: text;
117      background-clip: text;
118      color: transparent;
119    }
120
121    .lead {
122      font-size: clamp(1.05rem, 1.8vw, 1.3rem);
123      line-height: 1.5;
124      color: var(--ink-2);
125      max-width: 42em;
126    }
127
128    .muted { color: var(--ink-3); }
129    .small { font-size: 0.85rem; }
130
131    /* ---------- Buttons ---------- */
132    .btn {
133      display: inline-flex;
134      align-items: center;
135      justify-content: center;
136      gap: 8px;
137      border-radius: 999px;
138      padding: 14px 26px;
139      font-size: 0.98rem;
140      font-weight: 600;
141      line-height: 1;
142      cursor: pointer;
143      border: 1px solid transparent;
144      text-decoration: none;
145      transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
146      -webkit-tap-highlight-color: transparent;
147      font-family: inherit;
148    }
149    .btn:active { transform: scale(0.97); }
150    .btn-primary {
151      background: linear-gradient(120deg, #04805c, #065f46);
152      color: #ffffff;
153      box-shadow: 0 10px 28px rgba(6, 95, 70, 0.32);
154    }
155    .btn-primary:hover { transform: translateY(-1px); box-shadow: 0 14px 34px rgba(6, 95, 70, 0.4); }
156    .btn-ghost {
157      background: #ffffff;
158      border-color: #d2d2d7;
159      color: var(--ink);
160    }
161    .btn-ghost:hover { border-color: var(--brand); color: var(--brand-ink); }
162    .btn-sm { padding: 10px 18px; font-size: 0.88rem; }
163    .link-arrow {
164      display: inline-flex;
165      align-items: center;
166      gap: 6px;
167      color: var(--brand-ink);
168      font-weight: 600;
169      text-decoration: none;
170      font-size: 0.95rem;
171    }
172    .link-arrow:hover { text-decoration: underline; }
173
174    /* ---------- Header ---------- */
175    .site-header {
176      position: sticky;
177      top: 0;
178      z-index: 40;
179      background: rgba(255, 255, 255, 0.78);
180      backdrop-filter: blur(20px) saturate(180%);
181      -webkit-backdrop-filter: blur(20px) saturate(180%);
182      border-bottom: 1px solid rgba(0, 0, 0, 0.06);
183    }
184    .site-header .bar {
185      display: flex;
186      align-items: center;
187      justify-content: space-between;
188      gap: 16px;
189      height: 60px;
190    }
191    .brand {
192      display: flex;
193      align-items: center;
194      gap: 10px;
195      text-decoration: none;
196      color: var(--ink);
197      min-width: 0;
198    }
199    .brand { flex: 0 0 auto; }
200    .brand img { height: 30px; width: 30px; border-radius: 8px; }
201    .brand b { font-size: 1rem; font-weight: 700; letter-spacing: -0.02em; white-space: nowrap; }
202    .site-nav { display: flex; align-items: center; gap: 4px; }
203    .site-nav a {
204      color: var(--ink-2);
205      text-decoration: none;
206      font-size: 0.87rem;
207      font-weight: 500;
208      padding: 7px 11px;
209      border-radius: 999px;
210      transition: background-color 0.15s ease, color 0.15s ease;
211      white-space: nowrap;
212    }
213    .site-nav a:hover { background: rgba(0, 0, 0, 0.05); color: var(--ink); }
214    .header-cta { display: flex; align-items: center; gap: 10px; }
215    .header-cta .btn { white-space: nowrap; }
216    .lang-switch {
217      display: inline-flex;
218      align-items: center;
219      gap: 2px;
220      border: 1px solid var(--line);
221      border-radius: 999px;
222      padding: 2px;
223      background: #fff;
224    }
225    .lang-switch a, .lang-switch span {
226      display: inline-flex;
227      align-items: center;
228      justify-content: center;
229      min-width: 30px;
230      height: 26px;
231      border-radius: 999px;
232      font-size: 0.95rem;
233      text-decoration: none;
234      line-height: 1;
235    }
236    .lang-switch .active { background: var(--brand-soft); box-shadow: inset 0 0 0 1px rgba(5, 150, 105, 0.35); }
237
238    /* Collapsible mobile menu. On desktop the panel is transparent to layout
239       (display: contents), so nav and CTAs stay direct children of the bar. */
240    .nav-panel { display: contents; }
241    .nav-toggle {
242      display: none;
243      align-items: center;
244      justify-content: center;
245      flex: 0 0 auto;
246      width: 42px;
247      height: 42px;
248      padding: 0;
249      border: 1px solid var(--line);
250      border-radius: 13px;
251      background: #fff;
252      color: var(--ink);
253      cursor: pointer;
254      -webkit-tap-highlight-color: transparent;
255      transition: background-color 0.15s ease, border-color 0.15s ease;
256    }
257    .nav-toggle:hover { background: var(--bg-soft); border-color: #d8d8e0; }
258    .nav-toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
259    .nav-toggle .bars { position: relative; display: block; width: 18px; height: 12px; }
260    .nav-toggle .bars i {
261      position: absolute;
262      left: 0;
263      width: 100%;
264      height: 2px;
265      border-radius: 2px;
266      background: currentColor;
267      transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.18s ease;
268    }
269    .nav-toggle .bars i:nth-child(1) { top: 0; }
270    .nav-toggle .bars i:nth-child(2) { top: 5px; }
271    .nav-toggle .bars i:nth-child(3) { top: 10px; }
272    .nav-open .nav-toggle { background: var(--bg-soft); }
273    .nav-open .nav-toggle .bars i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
274    .nav-open .nav-toggle .bars i:nth-child(2) { opacity: 0; transform: scaleX(0.3); }
275    .nav-open .nav-toggle .bars i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
276
277    @media (max-width: 1104px) {
278      .site-header .bar { gap: 12px; }
279      .nav-toggle { display: inline-flex; }
280      .nav-panel {
281        display: block;
282        position: absolute;
283        top: 100%;
284        left: 0;
285        right: 0;
286        background: #fff;
287        border-bottom: 1px solid var(--line);
288        box-shadow: 0 26px 46px rgba(29, 29, 31, 0.12);
289        overflow: hidden;
290        max-height: 0;
291        opacity: 0;
292        visibility: hidden;
293        transition: max-height 0.36s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.22s ease, visibility 0s linear 0.36s;
294      }
295      .nav-open .nav-panel {
296        max-height: calc(100dvh - 60px);
297        opacity: 1;
298        visibility: visible;
299        overflow-y: auto;
300        transition: max-height 0.42s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.22s ease, visibility 0s;
301      }
302      .site-nav { flex-direction: column; align-items: stretch; gap: 2px; padding: 12px 24px 14px; }
303      .site-nav a {
304        padding: 13px 14px;
305        font-size: 1rem;
306        border-radius: 13px;
307        opacity: 0;
308        transform: translateY(-7px);
309        transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
310                    background-color 0.15s ease, color 0.15s ease;
311      }
312      .site-nav a[aria-current="page"] { background: var(--brand-soft); color: var(--brand-ink); font-weight: 600; }
313      .nav-open .site-nav a { opacity: 1; transform: none; }
314      .nav-open .site-nav a:nth-child(1) { transition-delay: 0.04s; }
315      .nav-open .site-nav a:nth-child(2) { transition-delay: 0.08s; }
316      .nav-open .site-nav a:nth-child(3) { transition-delay: 0.12s; }
317      .nav-open .site-nav a:nth-child(4) { transition-delay: 0.16s; }
318      .nav-open .site-nav a:nth-child(5) { transition-delay: 0.2s; }
319      .nav-open .site-nav a:nth-child(6) { transition-delay: 0.24s; }
320      .header-cta {
321        flex-wrap: wrap;
322        gap: 10px;
323        padding: 14px 24px 20px;
324        border-top: 1px solid var(--line);
325        opacity: 0;
326        transform: translateY(-7px);
327        transition: opacity 0.3s ease 0.2s, transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0.2s;
328      }
329      .nav-open .header-cta { opacity: 1; transform: none; }
330      .header-cta .btn { flex: 1 1 140px; justify-content: center; }
331      .lang-switch { flex: 0 0 auto; }
332    }
333    @media (prefers-reduced-motion: reduce) {
334      .nav-panel, .nav-toggle .bars i, .site-nav a, .header-cta {
335        transition-duration: 0.01ms !important;
336        transition-delay: 0s !important;
337      }
338    }
339
340    /* ---------- Hero ---------- */
341    .hero { padding: 84px 0 40px; text-align: center; position: relative; overflow: hidden; }
342    .hero::before {
343      content: "";
344      position: absolute;
345      inset: -40% -20% auto -20%;
346      height: 90%;
347      background:
348        radial-gradient(closest-side at 30% 40%, rgba(16, 185, 129, 0.10), transparent 70%),
349        radial-gradient(closest-side at 72% 30%, rgba(2, 132, 199, 0.09), transparent 70%);
350      pointer-events: none;
351    }
352    .hero > .wrap { position: relative; }
353    .hero-badge {
354      display: inline-flex;
355      align-items: center;
356      gap: 8px;
357      border: 1px solid rgba(5, 150, 105, 0.28);
358      background: var(--brand-soft);
359      color: var(--brand-ink);
360      border-radius: 999px;
361      padding: 7px 15px;
362      font-size: 0.82rem;
363      font-weight: 600;
364      margin-bottom: 26px;
365    }
366    .hero-badge .dot {
367      width: 7px; height: 7px; border-radius: 999px; background: #10b981;
368      box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.18);
369    }
370    .hero .lead { margin: 22px auto 0; }
371    .hero-ctas {
372      display: flex;
373      flex-wrap: wrap;
374      justify-content: center;
375      gap: 12px;
376      margin-top: 32px;
377    }
378    .hero-note { margin-top: 16px; font-size: 0.83rem; color: var(--ink-3); }
379    .hero-note a { color: inherit; }
380
381    /* ---------- Before/after compare slider ---------- */
382    .cmp-shell { margin: 56px auto 0; max-width: 880px; text-align: left; }
383    .cmp {
384      --pos: 55%;
385      position: relative;
386      border-radius: var(--radius);
387      border: 1px solid var(--line);
388      background: #fff;
389      box-shadow: var(--shadow-card);
390      overflow: hidden;
391      touch-action: pan-y;
392      user-select: none;
393      -webkit-user-select: none;
394    }
395    .cmp-doc { padding: 40px 28px 30px; font-size: 0.95rem; line-height: 1.75; color: #3a3a3f; }
396    .cmp-doc-head {
397      display: flex;
398      align-items: center;
399      gap: 8px;
400      padding: 12px 18px;
401      border-bottom: 1px solid var(--line);
402      background: #fafafa;
403      font-size: 0.78rem;
404      color: var(--ink-3);
405    }
406    .cmp-doc-head .dots { display: inline-flex; gap: 5px; margin-right: 6px; }
407    .cmp-doc-head .dots i { width: 9px; height: 9px; border-radius: 999px; background: #e3e3e8; }
408    .cmp-doc p { margin: 0 0 12px; }
409    .cmp-doc p:last-child { margin-bottom: 0; }
410    .cmp-pane-after { background: #fff; }
411    .cmp-pane-before {
412      position: absolute;
413      inset: 0;
414      background: #fff;
415      clip-path: inset(0 calc(100% - var(--pos)) 0 0);
416    }
417    .pii {
418      background: rgba(244, 63, 94, 0.12);
419      color: #be123c;
420      border-radius: 5px;
421      padding: 0 4px;
422      font-weight: 600;
423      font-style: normal;
424    }
425    .tag {
426      background: rgba(16, 185, 129, 0.14);
427      color: var(--brand-ink);
428      border-radius: 5px;
429      padding: 0 4px;
430      font-weight: 600;
431      font-size: 0.85em;
432      letter-spacing: 0.01em;
433    }
434    .cmp-handle {
435      position: absolute;
436      top: 0;
437      bottom: 0;
438      left: var(--pos);
439      width: 0;
440      z-index: 3;
441    }
442    .cmp-handle::before {
443      content: "";
444      position: absolute;
445      top: 0;
446      bottom: 0;
447      left: -1px;
448      width: 2px;
449      background: linear-gradient(180deg, #10b981, #0284c7);
450    }
451    .cmp-grip {
452      position: absolute;
453      top: 50%;
454      left: 0;
455      transform: translate(-50%, -50%);
456      width: 40px;
457      height: 40px;
458      border-radius: 999px;
459      background: #fff;
460      border: 1px solid var(--line);
461      box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
462      display: flex;
463      align-items: center;
464      justify-content: center;
465      color: var(--ink-2);
466      cursor: ew-resize;
467    }
468    .cmp-grip:focus-visible { outline: 3px solid rgba(14, 165, 233, 0.55); outline-offset: 2px; }
469    .cmp-label {
470      position: absolute;
471      top: 54px;
472      z-index: 2;
473      border-radius: 999px;
474      padding: 5px 12px;
475      font-size: 0.72rem;
476      font-weight: 700;
477      letter-spacing: 0.08em;
478      text-transform: uppercase;
479      pointer-events: none;
480    }
481    .cmp-label-before { left: 16px; background: rgba(244, 63, 94, 0.1); color: #be123c; }
482    .cmp-label-after { right: 16px; background: rgba(16, 185, 129, 0.12); color: var(--brand-ink); }
483    .cmp-caption {
484      display: flex;
485      flex-wrap: wrap;
486      justify-content: space-between;
487      gap: 8px;
488      margin-top: 14px;
489      font-size: 0.82rem;
490      color: var(--ink-3);
491    }
492    @media (max-width: 640px) {
493      .cmp-doc { padding: 42px 16px 20px; font-size: 0.86rem; }
494      .cmp-label { top: 48px; }
495    }
496
497    /* ---------- Customer reference logos (hero strip) ---------- */
498    .refs-band { --refh: 58px; max-width: 920px; margin: 30px auto 0; }
499    .refs-head {
500      margin: 0 0 14px;
501      font-size: 0.75rem;
502      font-weight: 600;
503      letter-spacing: 0.12em;
504      text-transform: uppercase;
505      color: var(--ink-3);
506    }
507    .refs {
508      /* The row height is reserved before the logos arrive, so filling the strip
509         never reflows the hero: that late jump is what used to read as a flicker. */
510      height: var(--refh);
511      overflow: hidden;
512      -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
513      mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
514    }
515    .refs-track {
516      display: flex;
517      align-items: center;
518      height: 100%;
519      width: max-content;
520      margin: 0;
521      padding: 0;
522      list-style: none;
523      opacity: 0;
524      transition: opacity 0.45s ease;
525      will-change: transform;
526    }
527    /* --refs-dur is measured up front and never changed again: re-timing a running
528       animation is what made the loop stutter. */
529    .refs.is-ready .refs-track {
530      opacity: 1;
531      animation: refs-scroll var(--refs-dur, 90s) linear infinite;
532    }
533    .refs-track li { flex: 0 0 auto; margin-right: 52px; display: flex; align-items: center; }
534    .refs-track li img { display: block; height: var(--refh); width: auto; opacity: 0.85; }
535    .ref-more {
536      white-space: nowrap;
537      font-size: 0.85rem;
538      font-weight: 600;
539      color: var(--ink-3);
540      border: 1px dashed #c7c7cf;
541      border-radius: 999px;
542      padding: 8px 16px;
543    }
544    @keyframes refs-scroll { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-50%, 0, 0); } }
545    @media (max-width: 640px) {
546      .refs-band { --refh: 44px; margin-top: 24px; }
547      .refs-track li { margin-right: 34px; }
548    }
549    @media (prefers-reduced-motion: reduce) {
550      .refs.is-ready .refs-track { animation: none; }
551      .refs { height: auto; overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
552    }
553
554    /* ---------- Stats strip ---------- */
555    .stats {
556      display: grid;
557      grid-template-columns: repeat(4, 1fr);
558      gap: 18px;
559      text-align: center;
560    }
561    .stats b {
562      display: block;
563      font-size: clamp(2rem, 4.2vw, 3rem);
564      font-weight: 800;
565      letter-spacing: -0.03em;
566      color: var(--ink);
567    }
568    .stats b span { display: inline; }
569    .stats > div > span { display: block; margin-top: 4px; font-size: 0.88rem; color: var(--ink-3); }
570    @media (max-width: 760px) { .stats { grid-template-columns: repeat(2, 1fr); gap: 28px 12px; } }
571
572    /* ---------- Cards ---------- */
573    .card {
574      background: #fff;
575      border: 1px solid var(--line);
576      border-radius: var(--radius);
577      box-shadow: var(--shadow-card);
578    }
579    .card-soft { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); }
580    .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
581    .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 18px; }
582    @media (max-width: 900px) { .grid-3 { grid-template-columns: 1fr; } .grid-2 { grid-template-columns: 1fr; } }
583
584    .sec-head { max-width: 720px; margin-bottom: 44px; }
585    .sec-head.center { margin-left: auto; margin-right: auto; text-align: center; }
586    .sec-head p { margin: 16px 0 0; font-size: 1.05rem; color: var(--ink-2); }
587
588    /* ---------- Demo widget ---------- */
589    .demo-card { padding: 26px 26px 30px; }
590    .demo-tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
591    .demo-tabs + .demo-tabs { margin-top: 8px; }
592    .demo-tab-label {
593      font-size: 0.65rem;
594      font-weight: 700;
595      letter-spacing: 0.14em;
596      text-transform: uppercase;
597      color: var(--ink-3);
598      min-width: 5.4rem;
599    }
600    .demo-tab {
601      border: 1px solid #d9d9e0;
602      background: #fff;
603      color: var(--ink-2);
604      border-radius: 999px;
605      padding: 6px 14px;
606      font-size: 0.8rem;
607      font-weight: 600;
608      cursor: pointer;
609      font-family: inherit;
610      transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
611    }
612    .demo-tab:hover { border-color: rgba(5, 150, 105, 0.5); color: var(--brand-ink); }
613    .demo-tab.active {
614      border-color: rgba(5, 150, 105, 0.55);
615      background: var(--brand-soft);
616      color: var(--brand-ink);
617      box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.2);
618    }
619    .demo-io { margin-top: 22px; display: flex; flex-direction: column; gap: 0; }
620    .demo-box { border: 1px solid var(--line); border-radius: 16px; padding: 16px 18px; background: #fafafa; }
621    .demo-box-label {
622      font-size: 0.68rem;
623      font-weight: 700;
624      letter-spacing: 0.13em;
625      text-transform: uppercase;
626      color: var(--ink-3);
627      margin-bottom: 8px;
628    }
629    .demo-box-out { background: rgba(16, 185, 129, 0.05); border-color: rgba(16, 185, 129, 0.3); }
630    .demo-box-out .demo-box-label { color: var(--brand-ink); }
631    .demo-box p { margin: 0; font-size: 0.93rem; line-height: 1.7; }
632    .demo-arrow { display: flex; align-items: center; justify-content: center; gap: 14px; padding: 12px 0; color: var(--ink-3); }
633    .demo-arrow img { height: 40px; width: 40px; border-radius: 10px; }
634    .demo-arrow .line { flex: 1; height: 1px; background: linear-gradient(90deg, transparent, #d9d9e0); }
635    .demo-arrow .line:last-child { background: linear-gradient(90deg, #d9d9e0, transparent); }
636    .demo-note {
637      margin: 18px 0 0;
638      font-size: 0.83rem;
639      line-height: 1.55;
640      color: var(--ink-3);
641      background: #fafafa;
642      border: 1px dashed #d9d9e0;
643      border-radius: 12px;
644      padding: 10px 14px;
645    }
646    .pii-src { background: rgba(244, 63, 94, 0.12); color: #be123c; border-radius: 5px; padding: 0 4px; font-weight: 600; font-style: normal; }
647    .pii-out { background: rgba(16, 185, 129, 0.14); color: var(--brand-ink); border-radius: 5px; padding: 0 4px; font-weight: 600; }
648
649    /* ---------- Entity chips ---------- */
650    .entity-grid { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 28px; }
651    .entity-grid span {
652      font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
653      font-size: 0.72rem;
654      color: var(--ink-2);
655      border: 1px solid var(--line);
656      background: #fff;
657      border-radius: 999px;
658      padding: 7px 13px;
659    }
660
661    /* ---------- Cloud workspace ---------- */
662    .cloud-app {
663      border-radius: var(--radius);
664      border: 1px solid var(--line);
665      background: #fff;
666      box-shadow: var(--shadow-card);
667      overflow: hidden;
668      text-align: left;
669    }
670    .cloud-chrome {
671      display: flex;
672      align-items: center;
673      gap: 10px;
674      padding: 11px 16px;
675      border-bottom: 1px solid var(--line);
676      background: #fafafa;
677    }
678    .cloud-chrome .dots { display: inline-flex; gap: 6px; }
679    .cloud-chrome .dots i { width: 10px; height: 10px; border-radius: 999px; }
680    .cloud-chrome .dots i:nth-child(1) { background: #fca5a5; }
681    .cloud-chrome .dots i:nth-child(2) { background: #fcd34d; }
682    .cloud-chrome .dots i:nth-child(3) { background: #86efac; }
683    .cloud-url {
684      flex: 1;
685      max-width: 340px;
686      margin: 0 auto;
687      background: #fff;
688      border: 1px solid var(--line);
689      border-radius: 8px;
690      font-size: 0.75rem;
691      color: var(--ink-3);
692      padding: 5px 12px;
693      text-align: center;
694      white-space: nowrap;
695      overflow: hidden;
696      text-overflow: ellipsis;
697    }
698    .cloud-spacer { width: 42px; flex: 0 0 auto; }
699    .cloud-body { display: grid; grid-template-columns: 250px 1fr; min-height: 400px; }
700    .cloud-side { border-right: 1px solid var(--line); background: #fbfbfd; padding: 16px 12px; }
701    .cloud-side-label {
702      font-size: 0.64rem;
703      font-weight: 700;
704      letter-spacing: 0.13em;
705      text-transform: uppercase;
706      color: var(--ink-3);
707      margin: 4px 8px 10px;
708    }
709    .model-btn {
710      display: flex;
711      align-items: center;
712      gap: 10px;
713      width: 100%;
714      border: 1px solid transparent;
715      background: transparent;
716      border-radius: 12px;
717      padding: 9px 10px;
718      cursor: pointer;
719      text-align: left;
720      font-family: inherit;
721      transition: background-color 0.15s ease, border-color 0.15s ease;
722    }
723    .model-btn:hover { background: rgba(0, 0, 0, 0.04); }
724    .model-btn.active { background: #fff; border-color: var(--line); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); }
725    .model-btn b { display: block; font-size: 0.82rem; color: var(--ink); font-weight: 600; line-height: 1.2; }
726    .model-btn span { display: block; font-size: 0.68rem; color: var(--ink-3); }
727    /* the icon tile and the tick have to out-specify ".model-btn span" above */
728    .model-btn .model-dot {
729      width: 26px;
730      height: 26px;
731      border-radius: 8px;
732      flex: 0 0 auto;
733      display: flex;
734      align-items: center;
735      justify-content: center;
736      color: #fff;
737    }
738    .model-dot svg { width: 16px; height: 16px; display: block; }
739    .model-dot-claude { background: #d97757; }
740    .model-dot-gpt { background: #10a37f; }
741    .model-dot-deepseek { background: #4d6bfe; }
742    .model-btn .model-check { margin-left: auto; color: var(--brand); font-weight: 700; visibility: hidden; }
743    .model-btn.active .model-check { visibility: visible; }
744    .cloud-side-note {
745      margin: 14px 8px 4px;
746      font-size: 0.7rem;
747      line-height: 1.5;
748      color: var(--ink-3);
749      border-top: 1px solid var(--line);
750      padding-top: 12px;
751    }
752    .cloud-main { padding: 20px 22px; display: flex; flex-direction: column; gap: 14px; }
753    .chat-msg { max-width: 92%; }
754    .chat-user { align-self: flex-end; }
755    .chat-user .bubble {
756      background: #f0f9f6;
757      border: 1px solid rgba(16, 185, 129, 0.25);
758      border-radius: 16px 16px 4px 16px;
759      padding: 12px 15px;
760      font-size: 0.86rem;
761      line-height: 1.6;
762    }
763    .chat-ai .bubble {
764      background: #fafafa;
765      border: 1px solid var(--line);
766      border-radius: 16px 16px 16px 4px;
767      padding: 12px 15px;
768      font-size: 0.86rem;
769      line-height: 1.6;
770    }
771    .chat-meta { display: flex; align-items: center; gap: 7px; font-size: 0.7rem; color: var(--ink-3); margin-bottom: 5px; }
772    .chat-user .chat-meta { justify-content: flex-end; }
773    .tag-real {
774      background: rgba(2, 132, 199, 0.12);
775      color: #0b4a6f;
776      border-radius: 5px;
777      padding: 0 4px;
778      font-weight: 600;
779      font-size: 0.85em;
780      letter-spacing: 0.01em;
781    }
782    /* The answer comes back with placeholders, which is all the model ever saw.
783       Mapping them back to the real values happens afterwards, on the device.
784       Both versions are stacked in one grid cell so the swap never reflows. */
785    .answer-swap { position: relative; display: grid; }
786    .answer-swap .ans { grid-area: 1 / 1; }
787    /* The placeholders dissolve first and the real values materialise after them,
788       so the two versions never overlap as double-struck text. */
789    .answer-swap .ans-anon { transition: opacity 0.3s ease, filter 0.3s ease; }
790    .answer-swap .ans-real { opacity: 0; transition: opacity 0.34s ease 0.24s; }
791    .answer-swap.is-refilled .ans-anon { opacity: 0; filter: blur(4px); }
792    .answer-swap.is-refilled .ans-real { opacity: 1; }
793    .answer-swap .ans-real .tag-real {
794      opacity: 0;
795      filter: blur(5px);
796      transform: translateY(2px);
797      transition: opacity 0.4s ease, filter 0.4s ease, transform 0.4s ease;
798    }
799    .answer-swap.is-refilled .ans-real .tag-real { opacity: 1; filter: none; transform: none; }
800    .answer-swap.is-refilled .ans-real .tag-real:nth-of-type(1) { transition-delay: 0.3s; }
801    .answer-swap.is-refilled .ans-real .tag-real:nth-of-type(2) { transition-delay: 0.44s; }
802    .answer-swap.is-refilled .ans-real .tag-real:nth-of-type(3) { transition-delay: 0.58s; }
803    .magic-sweep {
804      /* Absolutely placed in a grid container, so it is laid out against the
805         bubble's padding box; -1px takes it out over the border. */
806      position: absolute;
807      inset: -1px;
808      border-radius: inherit;
809      pointer-events: none;
810      opacity: 0;
811      background: linear-gradient(100deg, transparent 34%, rgba(16, 185, 129, 0.22) 48%,
812                  rgba(2, 132, 199, 0.20) 57%, transparent 72%);
813      background-size: 260% 100%;
814      background-position: 130% 0;
815    }
816    .answer-swap.is-refilling .magic-sweep { animation: magic-sweep 1.05s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
817    @keyframes magic-sweep {
818      0% { opacity: 0; background-position: 130% 0; }
819      18% { opacity: 1; }
820      82% { opacity: 1; }
821      100% { opacity: 0; background-position: -30% 0; }
822    }
823    .pipe-note-back {
824      opacity: 0;
825      transform: translateY(-5px) scale(0.96);
826      transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
827    }
828    .pipe-note-back.is-shown { opacity: 1; transform: none; }
829    @media (prefers-reduced-motion: reduce) {
830      .answer-swap .ans-anon, .answer-swap .ans-real, .answer-swap .ans-real .tag-real, .pipe-note-back {
831        transition-duration: 0.01ms !important;
832        transition-delay: 0s !important;
833      }
834      .answer-swap.is-refilling .magic-sweep { animation: none; }
835    }
836    .pipe-note[hidden] { display: none; }
837    .pipe-note {
838      display: flex;
839      align-items: center;
840      gap: 8px;
841      align-self: center;
842      font-size: 0.72rem;
843      font-weight: 600;
844      color: var(--brand-ink);
845      background: var(--brand-soft);
846      border: 1px solid rgba(5, 150, 105, 0.25);
847      border-radius: 999px;
848      padding: 5px 14px;
849    }
850    .pipe-note-back {
851      color: #0b4a6f;
852      background: rgba(2, 132, 199, 0.09);
853      border-color: rgba(2, 132, 199, 0.28);
854    }
855    .cloud-input {
856      margin-top: auto;
857      display: flex;
858      align-items: center;
859      gap: 10px;
860      border: 1px solid var(--line);
861      border-radius: 999px;
862      padding: 9px 10px 9px 18px;
863      color: var(--ink-3);
864      font-size: 0.85rem;
865      background: #fff;
866    }
867    .cloud-input .send {
868      margin-left: auto;
869      width: 30px;
870      height: 30px;
871      border-radius: 999px;
872      background: linear-gradient(120deg, #10b981, #059669);
873      color: #fff;
874      display: flex;
875      align-items: center;
876      justify-content: center;
877      font-size: 0.85rem;
878    }
879    @media (max-width: 820px) {
880      .cloud-body { grid-template-columns: 1fr; }
881      .cloud-side { border-right: 0; border-bottom: 1px solid var(--line); display: flex; gap: 6px; overflow-x: auto; padding: 10px; }
882      .cloud-side-label, .cloud-side-note { display: none; }
883      .model-btn { width: auto; flex: 0 0 auto; }
884      .model-btn span span { display: none; }
885      .model-check { display: none; }
886    }
887
888    /* ---------- Product cards ---------- */
889    .prod-card { padding: 26px 24px; display: flex; flex-direction: column; }
890    .prod-kicker { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 8px; }
891    .prod-card h3 { font-size: 1.25rem; letter-spacing: -0.02em; margin-bottom: 4px; }
892    .prod-card .plat { font-size: 0.76rem; color: var(--ink-3); margin-bottom: 14px; }
893    .feat-list { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; gap: 7px; }
894    .feat-list li { position: relative; padding-left: 22px; font-size: 0.86rem; color: var(--ink-2); line-height: 1.5; }
895    .feat-list li::before { content: "✓"; position: absolute; left: 0; top: 0; color: var(--brand); font-weight: 700; }
896    .prod-actions { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
897
898    /* ---------- Screenshots ---------- */
899    .shots { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 40px; }
900    @media (max-width: 760px) { .shots { grid-template-columns: 1fr; } }
901    .shots figure { margin: 0; }
902    .shots button {
903      display: block;
904      width: 100%;
905      padding: 0;
906      border: 1px solid var(--line);
907      border-radius: 16px;
908      overflow: hidden;
909      cursor: pointer;
910      background: #fff;
911      aspect-ratio: 3 / 2;
912      transition: border-color 0.2s ease, box-shadow 0.2s ease;
913    }
914    .shots button:hover { border-color: rgba(5, 150, 105, 0.5); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1); }
915    .shots img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
916    .shots figcaption { margin-top: 8px; font-size: 0.78rem; color: var(--ink-3); }
917
918    /* ---------- Use cases ---------- */
919    .uc-card { padding: 22px 22px 24px; }
920    .uc-card .ico { font-size: 1.4rem; margin-bottom: 10px; display: block; }
921    .uc-card h3 { font-size: 1.02rem; margin-bottom: 6px; letter-spacing: -0.01em; }
922    .uc-card p { margin: 0; font-size: 0.88rem; color: var(--ink-2); line-height: 1.55; }
923
924    /* ---------- Evidence ---------- */
925    .ev-label { margin: 0; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); }
926    /* All logos come from the shared jocapps set, which is already greyscale and
927       optically centred on a common canvas, so one height fits all four. */
928    .ev-logos { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 30px; margin: 16px 0 0; }
929    .ev-logos img { height: 62px; width: auto; filter: grayscale(1); opacity: 0.8; }
930    .ev-papers { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 24px; }
931    .ev-papers a {
932      font-size: 0.8rem;
933      color: var(--ink-2);
934      text-decoration: none;
935      border: 1px solid var(--line);
936      background: #fff;
937      border-radius: 999px;
938      padding: 7px 14px;
939      transition: border-color 0.15s ease, color 0.15s ease;
940    }
941    .ev-papers a:hover { border-color: rgba(5, 150, 105, 0.5); color: var(--brand-ink); }
942    .ev-figure {
943      display: block;
944      margin: 42px 0 0;
945      overflow: hidden;
946      border-radius: 18px;
947      border: 1px solid var(--line);
948      background: #fff;
949      text-decoration: none;
950      transition: border-color 0.15s ease, box-shadow 0.25s ease;
951    }
952    .ev-figure:hover { border-color: rgba(5, 150, 105, 0.45); box-shadow: var(--shadow-card); }
953    .ev-figure img { display: block; width: 100%; height: auto; padding: 18px 20px 6px; box-sizing: border-box; }
954    .ev-figure figcaption { padding: 8px 20px 16px; font-size: 0.8rem; line-height: 1.6; color: var(--ink-3); text-align: center; }
955    .ev-credit { max-width: 58em; margin: 34px 0 0; line-height: 1.75; }
956    .ev-credit + .ev-credit { margin-top: 10px; }
957    @media (max-width: 640px) {
958      .ev-logos { gap: 8px 22px; }
959      .ev-logos img { height: 48px; }
960      .ev-figure img { padding: 10px 10px 4px; }
961    }
962
963    /* ---------- Compare table (Free vs Pro) ---------- */
964    .cmp-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
965    .cmp-table { display: grid; grid-template-columns: minmax(11rem, 1.35fr) minmax(9rem, 1fr) minmax(11rem, 1.2fr); min-width: 36rem; border: 1px solid var(--line); border-radius: 16px; overflow: hidden; background: #fff; font-size: 0.8rem; }
966    .cmp-cell { padding: 9px 14px;
966 border-top: 1px solid #f0f0f4; color: var(--ink-2); display: flex; align-items: center; line-height: 1.45; }
967    .cmp-head { border-top: none; background: #fafafa; font-weight: 700; color: var(--ink); font-size: 0.76rem; }
968    .cmp-head-pro { background: rgba(16, 185, 129, 0.1); color: var(--brand-ink); }
969    .cmp-col-pro { background: rgba(16, 185, 129, 0.04); }
970    .cmp-feature { font-weight: 600; color: var(--ink); }
971    .cmp-yes { color: var(--brand-ink); font-weight: 700; }
972    .cmp-no { color: #86868b; }
973    .cmp-details { border: 1px solid var(--line); border-radius: 16px; background: #fff; }
974    .cmp-details > summary { cursor: pointer; padding: 15px 20px; font-size: 0.9rem; font-weight: 600; color: var(--ink-2); list-style: none; display: flex; align-items: center; gap: 8px; }
975    .cmp-details > summary::-webkit-details-marker { display: none; }
976    .cmp-details > summary::before { content: "▸"; color: #b0b0b8; transition: transform 0.15s ease; }
977    .cmp-details[open] > summary::before { transform: rotate(90deg); }
978    .cmp-details > summary:hover { color: var(--brand-ink); }
979    .cmp-details-body { padding: 0 20px 20px; }
980
981    /* ---------- FAQ ---------- */
982    .faq-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
983    @media (max-width: 760px) { .faq-grid { grid-template-columns: 1fr; } }
984    .faq-grid details { border: 1px solid var(--line); border-radius: 16px; background: #fff; padding: 16px 20px; }
985    .faq-grid summary { cursor: pointer; font-weight: 600; color: var(--ink); font-size: 0.95rem; list-style: none; }
986    .faq-grid summary::-webkit-details-marker { display: none; }
987    .faq-grid details p { margin: 10px 0 0; font-size: 0.88rem; color: var(--ink-2); }
988
989    /* ---------- CTA band ---------- */
990    .cta-band { text-align: center; padding: 96px 0; }
991    .cta-band .hero-ctas { margin-top: 30px; }
992
993    /* ---------- Sign in ---------- */
994    .signin-grid { display: grid; grid-template-columns: minmax(0, 400px) minmax(0, 1fr); gap: 60px; align-items: start; }
995    .signin-card {
996      background: #fff;
997      border: 1px solid var(--line);
998      border-radius: var(--radius);
999      box-shadow: var(--shadow-card);
1000      padding: 30px 28px 26px;
1001    }
1002    .signin-card h1 { font-size: 1.5rem; letter-spacing: -0.02em; }
1003    .signin-card > p.small { color: var(--ink-3); margin: 8px 0 22px; }
1004    .field { display: block; margin-bottom: 15px; }
1005    .field > span { display: block; font-size: 0.78rem; font-weight: 600; color: var(--ink-2); margin-bottom: 6px; }
1006    .field input {
1007      width: 100%;
1008      box-sizing: border-box;
1009      font: inherit;
1010      font-size: 0.95rem;
1011      color: var(--ink);
1012      background: #fff;
1013      border: 1px solid var(--line);
1014      border-radius: 12px;
1015      padding: 11px 14px;
1016      transition: border-color 0.15s ease, box-shadow 0.15s ease;
1017    }
1018    .field input::placeholder { color: #757580; } /* 4.6:1 on white, readable but still clearly a hint */
1019    .field input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
1020    .signin-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 2px 0 18px; }
1021    .signin-row label { display: inline-flex; align-items: center; gap: 7px; font-size: 0.8rem; color: var(--ink-2); }
1022    .signin-row a { font-size: 0.8rem; color: var(--brand-ink); }
1023    .signin-card .btn { width: 100%; }
1024    .signin-error {
1025      display: none;
1026      align-items: flex-start;
1027      gap: 9px;
1028      margin-bottom: 16px;
1029      padding: 11px 13px;
1030      border-radius: 12px;
1031      font-size: 0.84rem;
1032      line-height: 1.45;
1033      color: #9f1239;
1034      background: rgba(244, 63, 94, 0.08);
1035      border: 1px solid rgba(244, 63, 94, 0.3);
1036    }
1037    .signin-error.is-shown { display: flex; }
1038    .signin-error b { color: #881337; }
1039    .signin-alt {
1040      margin: 22px 0 0;
1041      padding-top: 18px;
1042      border-top: 1px solid var(--line);
1043      font-size: 0.85rem;
1044      color: var(--ink-3);
1045      text-align: center;
1046    }
1047    .signin-alt a { color: var(--brand-ink); font-weight: 600; }
1048    .signin-points { list-style: none; margin: 26px 0 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
1049    .signin-points li { position: relative; padding-left: 30px; font-size: 0.92rem; color: var(--ink-2); line-height: 1.55; }
1050    .signin-points li::before {
1051      content: "✓";
1052      position: absolute;
1053      left: 0;
1054      top: -1px;
1055      width: 20px;
1056      height: 20px;
1057      border-radius: 999px;
1058      background: var(--brand-soft);
1059      color: var(--brand-ink);
1060      font-size: 0.72rem;
1061      font-weight: 800;
1062      display: flex;
1063      align-items: center;
1064      justify-content: center;
1065    }
1066    .signin-points b { color: var(--ink); font-weight: 600; }
1067    @media (max-width: 900px) {
1068      .signin-grid { grid-template-columns: 1fr; gap: 34px; }
1069    }
1070
1071    /* ---------- Footer ---------- */
1072    .site-footer { border-top: 1px solid var(--line); background: var(--bg-soft); padding: 48px 0 40px; font-size: 0.83rem; }
1073    .footer-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: 32px; }
1074    @media (max-width: 760px) { .footer-grid { grid-template-columns: 1fr; } }
1075    .footer-links { display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start; }
1076    .footer-links a, .footer-links button {
1077      font-family: inherit;
1078      font-size: 0.8rem;
1079      color: var(--ink-2);
1080      background: #fff;
1081      border: 1px solid var(--line);
1082      border-radius: 999px;
1083      padding: 7px 14px;
1084      text-decoration: none;
1085      cursor: pointer;
1086      transition: border-color 0.15s ease, color 0.15s ease;
1087    }
1088    .footer-links a:hover, .footer-links button:hover { border-color: rgba(5, 150, 105, 0.5); color: var(--brand-ink); }
1089    .site-footer .fine { color: var(--ink-3); line-height: 1.6; }
1090    .site-footer .fine a { color: inherit; }
1091    .footer-about {
1092      padding: 24px;
1093      border: 1px solid var(--line);
1094      border-radius: 20px;
1095      background: rgba(255, 255, 255, 0.72);
1096      box-shadow: 0 14px 38px rgba(15, 23, 42, 0.06);
1097    }
1098    .footer-about .brand { width: fit-content; }
1099    .footer-about .fine + .fine { padding-top: 10px; border-top: 1px solid var(--line); }
1100    @media (max-width: 760px) { .footer-about { padding: 20px; } }
1101
1102    /* ---------- Reveal on scroll ---------- */
1103    .rv { opacity: 0; transform: translateY(22px); transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1), transform 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
1104    .rv.in { opacity: 1; transform: none; }
1105    @media (prefers-reduced-motion: reduce) {
1106      .rv { opacity: 1 !important; transform: none !important; transition: none !important; }
1107      html { scroll-behavior: auto; }
1108    }
1109
1110    /* ---------- Accessibility & modal plumbing ---------- */
1111    :where(a, button, input, select, textarea, iframe, summary):focus-visible {
1112      outline: 3px solid rgba(14, 165, 233, 0.55);
1113      outline-offset: 3px;
1114    }
1115    .skip-link {
1116      position: fixed;
1117      top: 0.75rem;
1118      left: 0.75rem;
1119      z-index: 100;
1120      transform: translateY(-160%);
1121      border-radius: 999px;
1122      background: #0f172a;
1123      color: #ffffff;
1124      padding: 0.65rem 1rem;
1125      font-size: 0.875rem;
1126      font-weight: 700;
1127      box-shadow: 0 10px 30px rgba(15, 23, 42, 0.2);
1128      transition: transform 0.2s ease;
1129    }
1130    .skip-link:focus { transform: translateY(0); }
1131    .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
1132    main section[id], main div[id] { scroll-margin-top: 5rem; }
1133    button, a { -webkit-tap-highlight-color: transparent; }
1134    button { touch-action: manipulation; }
1135    .modal-panel { overscroll-behavior: contain; }
1136    .shot-lightbox-panel { position: relative; width: min(760px, calc(100vw - 2rem)); max-height: min(70vh, 680px); }
1137    .shot-lightbox-image { width: 100%; max-height: min(70vh, 680px); object-fit: contain; }
1138    .shot-lightbox-close { position: absolute; top: -1rem; right: -1rem; z-index: 10; width: 2.75rem; height: 2.75rem; }
1139    @media (max-width: 767px) {
1140      .shot-lightbox-panel { max-height: 64vh; }
1141      .shot-lightbox-image { max-height: 64vh; }
1142      .shot-lightbox-close { top: -0.75rem; right: -0.5rem; }
1143    }
1144
1145    /* Light theme for the dialogs (markup uses dark slate utility classes) */
1146    .twp-glass-soft {
1147      background: #ffffff;
1148      border-radius: 22px;
1149      border: 1px solid var(--line);
1150      box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 24px 60px rgba(15, 23, 42, 0.18);
1151    }
1152    [role="dialog"] .bg-slate-950, [role="dialog"].bg-slate-950,
1153    [role="dialog"] .bg-slate-900 { background-color: #ffffff !important; }
1154    [role="dialog"] .text-white,
1155    [role="dialog"] .text-slate-50,
1156    [role="dialog"] .text-slate-100 { color: #0f172a !important; }
1157    [role="dialog"] .text-slate-200 { color: #334155 !important; }
1158    [role="dialog"] .text-slate-300 { color: #475569 !important; }
1159    [role="dialog"] .text-slate-400 { color: #64748b !important; }
1160    [role="dialog"] .text-emerald-300,
1161    [role="dialog"] .text-emerald-400 { color: #059669 !important; }
1162    [role="dialog"] .text-emerald-200 { color: #047857 !important; }
1163    [role="dialog"] .hover\:text-emerald-100:hover,
1164    [role="dialog"] .hover\:text-emerald-200:hover { color: #047857 !important; }
1165    [role="dialog"] .text-amber-200,
1166    [role="dialog"] .text-amber-300 { color: #b45309 !important; }
1167    [role="dialog"] .border-slate-800,
1168    [role="dialog"] .border-slate-700,
1169    [role="dialog"] .border-slate-600 { border-color: var(--line) !important; }
1170    [role="dialog"] .hover\:bg-slate-800:hover { background-color: #f1f5f9 !important; }
1171    [role="dialog"] input, [role="dialog"] select {
1172      background-color: #ffffff !important;
1173      color: #0f172a !important;
1174      border-color: #d2d2d7 !important;
1175    }
1176    [role="dialog"] .bg-emerald-400 { background-color: #059669 !important; color: #ffffff !important; }
1177    [role="dialog"] .hover\:bg-emerald-300:hover { background-color: #047857 !important; }
1178    [role="dialog"] .text-slate-950 { color: #ffffff !important; }
1179    /* Keep the screenshot lightbox itself dark for contrast */
1180    #shot-lightbox .bg-slate-950 { background-color: #0f172a !important; }
1181    #shot-lightbox .text-slate-100 { color: #f8fafc !important; }
1182  </style>
1183  <noscript><style>.rv { opacity: 1 !important; transform: none !important; }</style></noscript>
1184</head>
1185
1186
1187<body class="antialiased">
1188  <a href="#main-content" class="skip-link">Skip to main content</a>
1189
1190  <!-- Header -->
1191  <header id="top" class="site-header">
1192    <div class="wrap bar">
1193      <a href="/" aria-label="Textwash™ Pro home" class="brand">
1194        <img src="/logo.svg" alt="Textwash™ Pro logo" width="30" height="30" decoding="async" />
1195        <b>Textwash™&nbsp;Pro</b>
1196      </a>
1197
1198      <button type="button" class="nav-toggle" id="nav-toggle" aria-expanded="false"
1199              aria-controls="nav-panel" aria-label="Open menu"
1200              data-label-open="Open menu" data-label-close="Close menu">
1201        <span class="bars" aria-hidden="true"><i></i><i></i><i></i></span>
1202      </button>
1203
1204      <div class="nav-panel" id="nav-panel">
1205        <nav aria-label="Primary navigation" class="site-nav">
1206          <a href="/#products">Product</a>
1207          <a href="/cloud/" aria-current="page">Cloud</a>
1208          <a href="/#usecases">Use cases</a>
1209          <a href="/#research">Evidence</a>
1210          <a href="/partners/">Partners</a>
1211          <a href="/#faq">FAQ</a>
1212        </nav>
1213
1214        <div class="header-cta">
1215          <div class="lang-switch" role="group" aria-label="Language switcher">
1216            <span class="active" aria-current="true" title="English"><span class="sr-only">English (current language)</span><span aria-hidden="true">🇬🇧</span></span>
1217            <a href="/de/cloud/" lang="de" hreflang="de" aria-label="Zur deutschen Version wechseln" title="Deutsch">🇩🇪</a>
1218          </div>
1219          <a href="/" class="btn btn-ghost btn-sm">Back to homepage</a>
1220        </div>
1221      </div>
1222    </div>
1223  </header>
1224
1225  <main id="main-content">
1226    <section class="sec">
1227      <div class="wrap">
1228        <div class="signin-grid">
1229
1230          <div class="signin-card rv">
1231            <p class="eyebrow" style="margin-bottom:10px;">Textwash™ Pro Cloud</p>
1232            <h1>Sign in.</h1>
1233            <p class="small">Your AI workspace, anonymised before anything leaves it.</p>
1234
1235            <p class="signin-error" id="signin-error" role="alert" aria-live="assertive">
1236              <span aria-hidden="true">⚠️</span>
1237              <span><b>Login incorrect.</b> The e-mail address or password does not match an active
1238              Textwash™ Pro Cloud account. Please try again, or book a call to get access.</span>
1239            </p>
1240
1241            <form id="signin-form" novalidate>
1242              <label class="field">
1243                <span>E-mail address</span>
1244                <input type="email" id="signin-email" name="email" autocomplete="username"
1245                       placeholder="[email protected]" required />
1246              </label>
1247              <label class="field">
1248                <span>Password</span>
1249                <input type="password" id="signin-password" name="password" autocomplete="current-password"
1250                       placeholder="••••••••" required />
1251              </label>
1252
1253              <div class="signin-row">
1254                <label><input type="checkbox" id="signin-remember" /> Keep me signed in</label>
1255                <a href="mailto:[email protected]?subject=Textwash%E2%84%A2%20Pro%20Cloud%20password%20reset">Forgot password?</a>
1256              </div>
1257
1258              <button type="submit" id="signin-submit" class="btn btn-primary">Sign in</button>
1259            </form>
1260
1261            <p class="signin-alt">
1262              No account yet?
1263              <a href="https://calendar.jocapps.eu/textwashpro" target="_blank" rel="noopener noreferrer">Register and book a call ↗</a>
1264            </p>
1265          </div>
1266
1267          <div class="rv">
1268            <h2 class="h-lg">Frontier AI on documents that could never go to the cloud.</h2>
1269            <p class="lead" style="margin-top:16px;">
1270              A local AI removes personal data from every prompt on the way out, and can re-fill your
1271              real names into the answer on the way back, so the model itself never sees who anyone is.
1272              Anonymisation happens before any prompt leaves your workspace.
1273            </p>
1274            <ul class="signin-points">
1275              <li><b>Claude, ChatGPT, DeepSeek &amp; Google Gemini built in.</b> Pick the model per task; the anonymisation stays the same.</li>
1276              <li><b>Anonymised first, always.</b> Personal data is replaced before any prompt leaves your workspace.</li>
1277              <li><b>Re-fill with your data.</b> Placeholders are mapped back on your device, never in the cloud.</li>
1278              <li><b>A full AI workplace.</b> Summarise, translate, analyse, with team projects, dashboards and logs included.</li>
1279            </ul>
1280            <div class="hero-ctas" style="margin-top:30px;">
1281              <a href="https://calendar.jocapps.eu/textwashpro" target="_blank" rel="noopener noreferrer" class="btn btn-primary">Book a call</a>
1282              <a href="/#cloud" class="btn btn-ghost">See how it works</a>
1283            </div>
1284            <p class="small muted" style="margin-top:18px;">
1285              ISO 27001 &amp; ISO 9001 certified development since 2014, Made in Germany
1286            </p>
1287          </div>
1288
1289        </div>
1290      </div>
1291    </section>
1292  </main>
1293
1294  <!-- Footer -->
1295  <footer class="site-footer">
1296    <div class="wrap footer-grid">
1297      <div class="footer-about">
1298        <a href="/" aria-label="Textwash™ Pro home" class="brand" style="margin-bottom:14px;">
1299          <img src="/logo.svg" alt="Textwash™ Pro logo" width="30" height="30" />
1300          <b>Textwash™&nbsp;Pro</b>
1301        </a>
1302        <p class="fine" style="max-width:34em; margin:12px 0 0;">
1303          Automated text anonymisation for research, business &amp; public sector.<br />
1304          &copy; <a href="https://bkleinberg.net" target="_blank" rel="noopener noreferrer">Prof. Dr. Bennett Kleinberg</a> &amp;
1305          <a href="https://www.jocapps.com" target="_blank" rel="noopener noreferrer">jocapps<sup>&reg;</sup> GmbH</a>,
1306          ISO 27001 &amp; ISO 9001 certified development since 2014, Made in Germany 🇪🇺🇩🇪 with lots of ❤️ &amp; ☕️
1307        </p>
1308        <p class="fine" style="margin:8px 0 0;">
1309          Textwash™ is a registered trademark in the European Union (ID 019336966).
1310        </p>
1311      </div>
1312      <div class="footer-links">
1313        <a href="/">Textwash™ Pro Home</a>
1314        <a href="mailto:[email protected]">[email protected]</a>
1315        <a href="https://www.jocapps.com/legal_disclosure/" target="_blank" rel="noopener noreferrer">Legal Disclosure</a>
1316        <a href="https://www.jocapps.com/privacy/" target="_blank" rel="noopener noreferrer">Privacy Policy</a>
1317      </div>
1318    </div>
1319  </footer>
1320
1321  
1321<script>
1322    // Sign-in is not wired to an identity provider on this page: the real workspace lives
1323    // behind the account we set up on the call, so every attempt here reports back as failed.
1324    (function () {
1325      var form = document.getElementById('signin-form');
1326      var error = document.getElementById('signin-error');
1327      var submit = document.getElementById('signin-submit');
1328      if (!form || !error || !submit) return;
1329      var label = submit.textContent;
1330      form.addEventListener('submit', function (event) {
1331        event.preventDefault();
1332        error.classList.remove('is-shown');
1333        submit.disabled = true;
1334        submit.textContent = 'Signing in…';
1335        window.setTimeout(function () {
1336          submit.disabled = false;
1337          submit.textContent = label;
1338          error.classList.add('is-shown');
1339          var password = document.getElementById('signin-password');
1340          if (password) { password.value = ''; password.focus(); }
1341        }, 700);
1342      });
1343    })();
1344  </script>
1344
1345
1346  
1346<script>
1347    // Reveal-on-scroll
1348    (function () {
1349      var els = document.querySelectorAll('.rv');
1350      if (!('IntersectionObserver' in window) || (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches)) {
1351        els.forEach(function (el) { el.classList.add('in'); });
1352        return;
1353      }
1354      var io = new IntersectionObserver(function (entries) {
1355        entries.forEach(function (entry) {
1356          if (entry.isIntersecting) { entry.target.classList.add('in'); io.unobserve(entry.target); }
1357        });
1358      }, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' });
1359      var vh = window.innerHeight || document.documentElement.clientHeight;
1360      var i = 0;
1361      els.forEach(function (el) {
1362        if (el.getBoundingClientRect().top > vh * 0.9) {
1363          el.style.transitionDelay = (Math.min(i % 3, 2) * 70) + 'ms';
1364          i++;
1365          io.observe(el);
1366        } else {
1367          el.classList.add('in');
1368        }
1369      });
1370    })();
1371  </script>
1371
1372
1373  
1373<script>
1374    // Mobile menu: expand and collapse the header panel
1375    (function () {
1376      var header = document.querySelector('.site-header');
1377      var toggle = document.getElementById('nav-toggle');
1378      var panel = document.getElementById('nav-panel');
1379      if (!header || !toggle || !panel) return;
1380      var mq = window.matchMedia('(max-width: 1104px)');
1381
1382      function setOpen(open) {
1383        if (open === header.classList.contains('nav-open')) return;
1384        header.classList.toggle('nav-open', open);
1385        toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
1386        toggle.setAttribute('aria-label', toggle.getAttribute(open ? 'data-label-close' : 'data-label-open'));
1387      }
1388
1389      toggle.addEventListener('click', function () {
1390        setOpen(!header.classList.contains('nav-open'));
1391      });
1392      // Any link or button inside the panel navigates, so collapse it again.
1393      panel.addEventListener('click', function (e) {
1394        if (e.target.closest('a, button')) setOpen(false);
1395      });
1396      document.addEventListener('click', function (e) {
1397        if (!header.contains(e.target)) setOpen(false);
1398      });
1399      document.addEventListener('keydown', function (e) {
1400        if (e.key === 'Escape' || e.key === 'Esc') {
1401          if (header.classList.contains('nav-open')) { setOpen(false); toggle.focus(); }
1402        }
1403      });
1404      // Never leave the panel stuck open when the layout grows past the breakpoint.
1405      function onBreakpoint() { if (!mq.matches) setOpen(false); }
1406      if (mq.addEventListener) mq.addEventListener('change', onBreakpoint);
1407      else if (mq.addListener) mq.addListener(onBreakpoint);
1408    })();
1409  </script>
vendor: 1 bytes, line 1409
1409
1410<script>
1411!(function(){if(window.chatnode && window.chatnode('getState') === 'initialized')return;window.chatnode=(...args)=>{if(!window.chatnode.q)window.chatnode.q=[];window.chatnode.q.push(args);};window.chatnode=new Proxy(window.chatnode,{get(target,prop){if(prop==='q')return target.q;return(...args)=>target(prop,...args);},});function onLoad(){const script=document.createElement('script');script.src='https://embed.chatnode.ai/f75faf3a-adaa-4ac1-9222-ae1939865792/popup.js';document.body.appendChild(script);}if(document.readyState==='complete'){onLoad();}else{window.addEventListener('load',onLoad);}})();
1412</script>
1412
1413</body>
1414</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.