Files
boc/html-patches/company-onboarding.html
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

1200 lines
46 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="sv">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bolagsonboarding — AAMOS</title>
<style>
/* ── iOS 26 Native Design System ── */
:root {
--ios-bg: #F2F2F7;
--ios-surface: #FFFFFF;
--ios-surface-2: #F2F2F7;
--ios-grouped: rgba(255,255,255,0.72);
--ios-label: #000000;
--ios-label-2: rgba(60,60,67,0.60);
--ios-label-3: rgba(60,60,67,0.30);
--ios-label-4: rgba(60,60,67,0.18);
--ios-separator: rgba(60,60,67,0.29);
--ios-fill: rgba(120,120,128,0.12);
--ios-fill-2: rgba(120,120,128,0.16);
--ios-blue: #007AFF;
--ios-blue-dark: #0056CC;
--ios-green: #34C759;
--ios-orange: #FF9500;
--ios-red: #FF3B30;
--ios-purple: #AF52DE;
--ios-teal: #5AC8FA;
--ios-indigo: #5856D6;
--ios-gray-6: #F2F2F7;
--ios-corner-large: 16px;
--ios-corner-med: 12px;
--ios-corner-small: 8px;
--ios-shadow: 0 2px 20px rgba(0,0,0,0.08);
--ios-shadow-lg: 0 8px 40px rgba(0,0,0,0.12);
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--ios-bg);
color: var(--ios-label);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
/* ── Navigation Bar ── */
.nav-bar {
position: sticky; top: 0; z-index: 100;
background: rgba(242,242,247,0.88);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 0.5px solid var(--ios-separator);
padding: 0 20px;
height: 56px;
display: flex; align-items: center; justify-content: space-between;
}
.nav-title {
font-size: 17px; font-weight: 600;
letter-spacing: -0.4px;
}
.nav-logo {
font-size: 15px; font-weight: 500; color: var(--ios-blue);
text-decoration: none;
}
.nav-badge {
background: var(--ios-blue); color: #fff;
font-size: 11px; font-weight: 600;
padding: 2px 8px; border-radius: 10px;
}
/* ── Hero ── */
.hero {
background: linear-gradient(145deg, #007AFF 0%, #5856D6 60%, #AF52DE 100%);
padding: 56px 24px 48px;
text-align: center;
color: #fff;
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute; inset: 0;
background: radial-gradient(ellipse at 30% 60%, rgba(255,255,255,0.15) 0%, transparent 60%);
}
.hero-eyebrow {
font-size: 13px; font-weight: 600;
letter-spacing: 0.8px; text-transform: uppercase;
opacity: 0.8; margin-bottom: 12px;
}
.hero-title {
font-size: 34px; font-weight: 700;
letter-spacing: -1.2px; line-height: 1.1;
margin-bottom: 12px;
}
.hero-sub {
font-size: 17px; opacity: 0.85;
font-weight: 400; line-height: 1.5;
max-width: 340px; margin: 0 auto 24px;
}
.hero-badge {
display: inline-flex; align-items: center; gap: 6px;
background: rgba(255,255,255,0.2);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.3);
border-radius: 100px;
padding: 6px 16px;
font-size: 14px; font-weight: 600;
}
/* ── Container ── */
.container {
max-width: 520px; margin: 0 auto;
padding: 0 16px 80px;
}
/* ── Step Indicator ── */
.steps-bar {
display: flex; align-items: center; justify-content: center;
gap: 0; margin: 24px 0;
}
.step-dot {
width: 28px; height: 28px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 12px; font-weight: 700;
background: var(--ios-fill);
color: var(--ios-label-2);
transition: all 0.3s ease;
position: relative; z-index: 1;
}
.step-dot.active {
background: var(--ios-blue); color: #fff;
box-shadow: 0 4px 12px rgba(0,122,255,0.35);
}
.step-dot.done {
background: var(--ios-green); color: #fff;
}
.step-line {
flex: 1; height: 2px; max-width: 48px;
background: var(--ios-fill);
transition: background 0.3s;
}
.step-line.done { background: var(--ios-green); }
/* ── Card ── */
.card {
background: var(--ios-surface);
border-radius: var(--ios-corner-large);
box-shadow: var(--ios-shadow);
overflow: hidden;
margin-bottom: 16px;
}
.card-header {
padding: 16px 20px 12px;
border-bottom: 0.5px solid var(--ios-separator);
display: flex; align-items: center; gap: 12px;
}
.card-icon {
width: 36px; height: 36px; border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 18px;
flex-shrink: 0;
}
.card-icon.blue { background: rgba(0,122,255,0.12); }
.card-icon.green { background: rgba(52,199,89,0.12); }
.card-icon.purple { background: rgba(175,82,222,0.12); }
.card-title { font-size: 17px; font-weight: 600; letter-spacing: -0.4px; }
.card-sub { font-size: 13px; color: var(--ios-label-2); margin-top: 2px; }
.card-body { padding: 16px 20px; }
/* ── Form fields ── */
.field-group { margin-bottom: 16px; }
.field-label {
font-size: 13px; font-weight: 500;
color: var(--ios-label-2);
margin-bottom: 6px;
letter-spacing: -0.1px;
display: flex; align-items: center; gap: 6px;
}
.field-required { color: var(--ios-red); }
.field-input {
width: 100%;
background: var(--ios-fill);
border: none; outline: none;
border-radius: var(--ios-corner-med);
padding: 13px 16px;
font-size: 17px; font-weight: 400;
color: var(--ios-label);
font-family: inherit;
transition: all 0.2s;
-webkit-appearance: none;
}
.field-input:focus {
background: var(--ios-surface);
box-shadow: 0 0 0 2px var(--ios-blue), var(--ios-shadow);
}
.field-input.valid { box-shadow: 0 0 0 2px var(--ios-green); }
.field-input.error { box-shadow: 0 0 0 2px var(--ios-red); }
.field-hint {
font-size: 12px; color: var(--ios-label-3);
margin-top: 6px;
}
/* ── Company preview card ── */
.company-preview {
background: linear-gradient(135deg, rgba(52,199,89,0.08) 0%, rgba(0,122,255,0.06) 100%);
border: 1px solid rgba(52,199,89,0.3);
border-radius: var(--ios-corner-med);
padding: 14px 16px;
margin-top: 12px;
display: none;
}
.company-preview.show { display: block; animation: fadeIn 0.3s ease; }
.company-preview-name {
font-size: 17px; font-weight: 600; letter-spacing: -0.5px;
color: var(--ios-label);
}
.company-preview-row {
font-size: 13px; color: var(--ios-label-2);
margin-top: 4px; display: flex; align-items: center; gap: 6px;
}
.company-preview-badge {
display: inline-flex; align-items: center;
background: rgba(52,199,89,0.15); color: #1a7a30;
border-radius: 100px; padding: 2px 8px;
font-size: 11px; font-weight: 600; letter-spacing: 0.2px;
}
/* ── Plan selector ── */
.plans-grid {
display: grid; grid-template-columns: 1fr 1fr 1fr;
gap: 10px; margin-bottom: 4px;
}
.plan-card {
border: 1.5px solid var(--ios-fill-2);
border-radius: var(--ios-corner-med);
padding: 12px 10px;
cursor: pointer; text-align: center;
transition: all 0.2s;
background: var(--ios-surface);
position: relative;
}
.plan-card:hover { border-color: var(--ios-blue); background: rgba(0,122,255,0.04); }
.plan-card.selected {
border-color: var(--ios-blue);
background: rgba(0,122,255,0.08);
box-shadow: 0 4px 16px rgba(0,122,255,0.15);
}
.plan-name { font-size: 14px; font-weight: 600; margin-top: 4px; }
.plan-price { font-size: 12px; color: var(--ios-label-2); margin-top: 2px; }
.plan-emoji { font-size: 20px; }
.plan-check {
position: absolute; top: 6px; right: 6px;
width: 16px; height: 16px; border-radius: 50%;
background: var(--ios-blue); color: #fff;
font-size: 10px;
display: none; align-items: center; justify-content: center;
}
.plan-card.selected .plan-check { display: flex; }
/* ── CTA Button ── */
.btn-primary {
width: 100%;
background: var(--ios-blue);
color: #fff;
border: none; outline: none;
border-radius: var(--ios-corner-large);
padding: 16px;
font-size: 17px; font-weight: 600;
font-family: inherit;
cursor: pointer;
letter-spacing: -0.4px;
transition: all 0.15s;
box-shadow: 0 4px 16px rgba(0,122,255,0.35);
-webkit-tap-highlight-color: transparent;
}
.btn-primary:hover { background: var(--ios-blue-dark); box-shadow: 0 6px 24px rgba(0,122,255,0.4); }
.btn-primary:active { transform: scale(0.98); }
.btn-primary:disabled { background: var(--ios-label-3); box-shadow: none; cursor: not-allowed; }
.btn-secondary {
background: var(--ios-fill);
color: var(--ios-blue);
border: none; outline: none;
border-radius: var(--ios-corner-large);
padding: 14px 24px;
font-size: 15px; font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: all 0.15s;
-webkit-tap-highlight-color: transparent;
}
.btn-secondary:hover { background: var(--ios-fill-2); }
/* ── Progress bar ── */
.progress-wrap {
background: var(--ios-fill);
border-radius: 100px; height: 6px;
overflow: hidden; margin: 12px 0;
}
.progress-bar {
height: 100%; border-radius: 100px;
background: linear-gradient(90deg, var(--ios-blue) 0%, var(--ios-indigo) 100%);
transition: width 0.5s ease;
box-shadow: 0 2px 8px rgba(0,122,255,0.35);
}
/* ── Step items ── */
.step-items { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
.step-item {
display: flex; align-items: center; gap: 12px;
padding: 10px 0;
border-bottom: 0.5px solid var(--ios-separator);
animation: fadeIn 0.4s ease;
}
.step-item:last-child { border-bottom: none; }
.step-status-icon {
width: 28px; height: 28px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0; font-size: 14px;
}
.step-status-icon.pending { background: var(--ios-fill); color: var(--ios-label-3); }
.step-status-icon.running { background: rgba(0,122,255,0.12); color: var(--ios-blue); }
.step-status-icon.done { background: rgba(52,199,89,0.12); color: var(--ios-green); }
.step-status-icon.error { background: rgba(255,59,48,0.12); color: var(--ios-red); }
.step-item-text { flex: 1; }
.step-item-label { font-size: 15px; font-weight: 500; }
.step-item-sub { font-size: 12px; color: var(--ios-label-2); margin-top: 2px; }
.spinner { animation: spin 1s linear infinite; display: inline-block; }
/* ── Checklist ── */
.checklist-category { margin-bottom: 20px; }
.checklist-cat-header {
display: flex; align-items: center; gap: 8px;
font-size: 13px; font-weight: 600;
color: var(--ios-label-2); text-transform: uppercase;
letter-spacing: 0.5px; margin-bottom: 8px;
}
.checklist-item {
display: flex; align-items: flex-start; gap: 12px;
padding: 10px 0;
border-bottom: 0.5px solid var(--ios-separator);
cursor: pointer;
transition: opacity 0.2s;
}
.checklist-item:last-child { border-bottom: none; }
.checklist-item.completed { opacity: 0.5; }
.checklist-item.completed .checklist-title { text-decoration: line-through; }
.checklist-checkbox {
width: 22px; height: 22px; border-radius: 50%;
border: 2px solid var(--ios-separator);
display: flex; align-items: center; justify-content: center;
flex-shrink: 0; margin-top: 1px;
transition: all 0.2s;
}
.checklist-item.completed .checklist-checkbox {
background: var(--ios-green); border-color: var(--ios-green);
}
.checklist-item.auto .checklist-checkbox {
background: var(--ios-blue); border-color: var(--ios-blue);
}
.checklist-title { font-size: 15px; font-weight: 500; }
.checklist-meta { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.checklist-time { font-size: 12px; color: var(--ios-label-2); }
.priority-badge {
font-size: 10px; font-weight: 600; letter-spacing: 0.3px;
padding: 2px 6px; border-radius: 4px;
text-transform: uppercase;
}
.priority-high { background: rgba(255,59,48,0.1); color: var(--ios-red); }
.priority-medium { background: rgba(255,149,0,0.1); color: var(--ios-orange); }
.priority-low { background: rgba(120,120,128,0.1); color: var(--ios-label-2); }
/* ── Success state ── */
.success-hero {
text-align: center; padding: 32px 20px 24px;
}
.success-icon {
width: 80px; height: 80px; border-radius: 50%;
background: linear-gradient(135deg, var(--ios-green), #28a745);
margin: 0 auto 20px;
display: flex; align-items: center; justify-content: center;
font-size: 36px;
box-shadow: 0 8px 32px rgba(52,199,89,0.35);
animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.success-title {
font-size: 28px; font-weight: 700; letter-spacing: -0.8px;
margin-bottom: 8px;
}
.success-sub {
font-size: 15px; color: var(--ios-label-2); line-height: 1.5;
}
/* ── Stats row ── */
.stats-row {
display: grid; grid-template-columns: 1fr 1fr 1fr;
gap: 12px; margin-bottom: 16px;
}
.stat-card {
background: var(--ios-surface);
border-radius: var(--ios-corner-med);
padding: 14px;
text-align: center;
box-shadow: var(--ios-shadow);
}
.stat-value { font-size: 24px; font-weight: 700; letter-spacing: -0.5px; }
.stat-label { font-size: 11px; color: var(--ios-label-2); font-weight: 500; margin-top: 2px; }
/* ── Hidden states ── */
.step-view { display: none; }
.step-view.active { display: block; }
/* ── Animations ── */
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes popIn { from { transform: scale(0.5); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes spin { to { transform: rotate(360deg); } }
.fade-in { animation: fadeIn 0.4s ease forwards; }
/* ── Utility ── */
.text-center { text-align: center; }
.mt-8 { margin-top: 8px; }
.mt-12 { margin-top: 12px; }
.mt-16 { margin-top: 16px; }
.gap-8 { gap: 8px; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.w-full { width: 100%; }
.section-header {
font-size: 22px; font-weight: 700;
letter-spacing: -0.6px; margin-bottom: 4px;
}
.section-sub {
font-size: 14px; color: var(--ios-label-2); margin-bottom: 16px; line-height: 1.5;
}
.tag {
display: inline-flex; align-items: center;
font-size: 12px; font-weight: 600;
padding: 3px 10px; border-radius: 100px;
}
.tag-blue { background: rgba(0,122,255,0.12); color: var(--ios-blue); }
.tag-green { background: rgba(52,199,89,0.12); color: #1a7a30; }
.tag-orange { background: rgba(255,149,0,0.12); color: #cc7700; }
/* Divider */
.divider { height: 0.5px; background: var(--ios-separator); margin: 12px 0; }
</style>
<link rel="stylesheet" href="/_design-system/apple-native/tokens.css">
<link rel="stylesheet" href="/_design-system/apple-native/universal-fix.css">
<link rel="stylesheet" href="/_design-system/apple-native/typography.css">
<link rel="stylesheet" href="/_design-system/apple-native/components.css">
<style>
/* ── Ouroboros OS Layout Inject ── */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#F2F2F7;--bg2:#FFFFFF;--bg3:#E5E5EA;
--accent:#007AFF;--accent2:#0051D4;
--text:#000000;--text2:rgba(60,60,67,.8);--text3:rgba(60,60,67,.6);
--card:#FFFFFF;--border:rgba(60,60,67,.13);
--green:#34C759;--red:#FF3B30;--orange:#FF9500;
--blue:#007AFF;--purple:#AF52DE;--teal:#30B0C7;--gold:#C8763A;
--shadow:0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.04);
}
@media(prefers-color-scheme:dark){:root{
--bg:#000;--bg2:#1C1C1E;--bg3:#2C2C2E;
--text:#fff;--text2:rgba(235,235,245,.8);--text3:rgba(235,235,245,.5);
--card:#1C1C1E;--border:rgba(255,255,255,.12);
--green:#30D158;--red:#FF453A;--orange:#FF9F0A;
--blue:#0A84FF;--purple:#BF5AF2;--shadow:0 2px 12px rgba(0,0,0,.4);
}}
html,body{height:100%;overflow:hidden}
body{font-family:-apple-system,'SF Pro Text',system-ui,sans-serif;-webkit-font-smoothing:antialiased;background:var(--bg);color:var(--text)}
/* ── Layout ── */
.os-layout{display:flex;height:100vh;overflow:hidden}
/* ── Sidebar ── */
.os-sidebar{width:240px;background:var(--bg2);border-right:.5px solid var(--border);display:flex;flex-direction:column;flex-shrink:0;overflow-y:auto;transition:transform .3s}
.os-sidebar-logo{padding:18px 18px 14px;border-bottom:.5px solid var(--border)}
.os-logo-brand{font-size:10px;font-weight:700;letter-spacing:.12em;color:var(--gold);text-transform:uppercase;margin-bottom:4px}
.os-logo-name{font-size:17px;font-weight:800;color:var(--text);letter-spacing:-.02em}
.os-logo-sub{font-size:11px;color:var(--text3);margin-top:2px}
.os-sidebar-back{padding:8px 14px;border-bottom:.5px solid var(--border)}
.os-sidebar-back a{color:var(--blue);font-size:13px;text-decoration:none;display:flex;align-items:center;gap:4px}
.os-sidebar-back a:hover{opacity:.7}
.os-sidebar-section{padding:12px 10px 4px}
.os-sidebar-label{font-size:10px;font-weight:700;letter-spacing:.1em;color:var(--text3);text-transform:uppercase;padding:0 8px;margin-bottom:6px}
.os-nav-item{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:10px;cursor:pointer;color:var(--text2);font-size:13px;font-weight:500;transition:all .15s;border:none;background:none;width:100%;text-align:left;text-decoration:none}
.os-nav-item:hover{background:var(--bg3);color:var(--text)}
.os-nav-item.active{background:var(--blue);color:#fff}
.os-nav-icon{font-size:15px;width:20px;text-align:center}
.os-sidebar-footer{margin-top:auto;padding:14px;border-top:.5px solid var(--border);font-size:11px;color:var(--text3)}
/* ── Main + Topbar ── */
.os-main{flex:1;display:flex;flex-direction:column;overflow:hidden}
.os-topbar{background:rgba(242,242,247,.85);backdrop-filter:saturate(180%) blur(20px);-webkit-backdrop-filter:saturate(180%) blur(20px);border-bottom:.5px solid var(--border);padding:0 20px;height:52px;display:flex;align-items:center;justify-content:space-between;position:relative;z-index:100;flex-shrink:0}
@media(prefers-color-scheme:dark){.os-topbar{background:rgba(28,28,30,.85)}}
.os-topbar-title{font-size:15px;font-weight:700;color:var(--text)}
.os-topbar-breadcrumb{font-size:12px;color:var(--text3);display:flex;align-items:center;gap:6px}
.os-topbar-actions{display:flex;gap:8px;align-items:center}
.os-topbar-org{font-size:12px;color:var(--text3);background:var(--bg3);padding:3px 9px;border-radius:8px;font-weight:600}
.os-topbar-live{display:inline-flex;align-items:center;gap:5px;background:rgba(52,199,89,.12);border:.5px solid rgba(52,199,89,.3);border-radius:20px;padding:3px 10px;font-size:11px;font-weight:700;color:var(--green)}
.os-live-dot{width:6px;height:6px;border-radius:50%;background:var(--green);animation:osPulse 1.5s infinite}
@keyframes osPulse{0%,100%{opacity:1}50%{opacity:.3}}
/* ── Content area ── */
.os-content{flex:1;overflow-y:auto;min-height:0}
/* ── Mobile ── */
@media(max-width:768px){.os-sidebar{display:none}.os-layout{flex-direction:column}}
</style>
<style>
/* iOS Perfection Fix — hides duplicate nav, fixes stats, improves typography */
.nav-bar{display:none!important}
.large-title{font-size:28px!important;font-weight:700!important;letter-spacing:-.5px!important;margin-bottom:3px!important}
.large-subtitle{font-size:14px!important;color:rgba(60,60,67,.55)!important;font-weight:400!important}
.large-title-area,.large-title-section{padding:4px 20px 8px!important}
.mini-stat{background:#fff!important;border-radius:14px!important;padding:14px 12px!important;border:none!important;box-shadow:0 1px 3px rgba(0,0,0,.05),0 2px 8px rgba(0,0,0,.04)!important}
.mini-stat-value{font-size:26px!important;font-weight:800!important;letter-spacing:-.03em!important;color:#007AFF!important;display:block!important;margin-bottom:5px!important}
.mini-stat-label{font-size:11px!important;font-weight:500!important;color:rgba(60,60,67,.55)!important;display:block!important}
.stats-row{gap:10px!important;padding:8px 16px 12px!important}
.search-area{padding:4px 16px 8px!important}
.search-wrap{height:38px!important;border-radius:11px!important;background:rgba(120,120,128,.11)!important}
.section-title-text,.section-title{font-size:20px!important;font-weight:700!important;letter-spacing:-.3px!important}
.kpi-card{background:#fff!important;border-radius:16px!important;border:none!important;box-shadow:0 1px 3px rgba(0,0,0,.05),0 2px 8px rgba(0,0,0,.04)!important}
.kpi-value{font-weight:800!important;letter-spacing:-.03em!important}
@media(prefers-color-scheme:dark){
.mini-stat,.kpi-card{background:#1C1C1E!important}
.mini-stat-value{color:#0A84FF!important}
.large-title{color:#fff!important}
.large-subtitle{color:rgba(235,235,245,.55)!important}
}
</style>
<style id="ouroboros-apple-system">
/* OUROBOROS GLOBAL SYSTEM CSS — Apple iOS 18 baseline */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
/* System font stack — SF Pro on Apple, Inter elsewhere */
body, input, select, textarea, button {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro Display',
'Helvetica Neue', Arial, system-ui, sans-serif;
}
/* Fix any broken button appearances */
button { -webkit-appearance: none; appearance: none; cursor: pointer; }
select { -webkit-appearance: none; appearance: none; }
input[type="number"] { -moz-appearance: textfield; }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; }
/* Remove blue outline on focus (replace with subtle ring) */
:focus-visible {
outline: 2.5px solid rgba(0, 122, 255, 0.6);
outline-offset: 2px;
border-radius: 6px;
}
/* Language buttons must ALWAYS look like pills */
.lang-btn {
padding: 6px 12px !important;
border: 1.5px solid rgba(60,60,67,.15) !important;
border-radius: 20px !important;
background: #fff !important;
color: rgba(60,60,67,.6) !important;
font-size: 12px !important;
font-weight: 700 !important;
-webkit-appearance: none !important;
box-shadow: 0 1px 2px rgba(0,0,0,.06) !important;
}
.lang-btn.active {
background: #007AFF !important;
border-color: #007AFF !important;
color: #fff !important;
}
/* Tap targets minimum 44×44pt (iOS HIG requirement) */
@media (pointer: coarse) {
button, .btn-primary, .btn-ghost, .option-card, .lang-btn {
min-height: 44px;
}
}
</style>
<style id="wcag-contrast-enforcement">
/* AAMOS GLOBAL CONTRAST ENFORCEMENT — WCAG AA (4.5:1 minimum) */
/* Regel: Ingen text under 70% opacity. Aldrig. */
body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
/* Fix common low-contrast patterns */
[style*="rgba(60,60,67,0.4)"] { color: rgba(60,60,67,0.65) !important; }
[style*="rgba(60,60,67,0.3)"] { color: rgba(60,60,67,0.65) !important; }
[style*="rgba(255,255,255,0.4)"] { color: rgba(255,255,255,0.72) !important; }
[style*="rgba(255,255,255,0.3)"] { color: rgba(255,255,255,0.72) !important; }
[style*="opacity: 0.4"] { opacity: 0.72 !important; }
[style*="opacity: 0.3"] { opacity: 0.72 !important; }
</style>
</head>
<body>
<div class="os-layout">
<!-- ── Sidebar ── -->
<nav class="os-sidebar">
<div class="os-sidebar-logo">
<div class="os-logo-brand">AAMOS · Ouroboros</div>
<div class="os-logo-name">Bolagsonboarding</div>
<div class="os-logo-sub">AAMOS Ouroboros</div>
</div>
<div class="os-sidebar-back">
<a href="/ouroboros/">← Ouroboros Hub</a>
</div>
<div class="os-sidebar-section">
<div class="os-sidebar-label">Navigation</div>
<a class="os-nav-item active" href="#">
<span class="os-nav-icon">🏛️</span> Bolagsonboarding
</a>
<a class="os-nav-item" href="/ouroboros/cockpit.html">
<span class="os-nav-icon">🚀</span> Cockpit
</a>
<a class="os-nav-item" href="/ouroboros/finance-hub.html">
<span class="os-nav-icon">💰</span> Finance Hub
</a>
<a class="os-nav-item" href="/ouroboros/compliance-hub.html">
<span class="os-nav-icon">⚖️</span> Compliance Hub
</a>
<a class="os-nav-item" href="/ouroboros/analytics-hub.html">
<span class="os-nav-icon">📊</span> Analytics Hub
</a>
</div>
<div class="os-sidebar-footer">
<div>AAMOS Ouroboros OS</div>
<div style="margin-top:4px;opacity:.6">wavult-group</div>
</div>
</nav>
<!-- ── Main ── -->
<div class="os-main">
<div class="os-topbar">
<div class="os-topbar-breadcrumb">
<a href="/ouroboros/" style="color:var(--blue);text-decoration:none">Ouroboros</a>
<span></span>
<span class="os-topbar-title">Bolagsonboarding</span>
</div>
<div class="os-topbar-actions">
<span class="os-topbar-live"><span class="os-live-dot"></span>LIVE</span>
<span class="os-topbar-org">wavult-group</span>
</div>
</div>
<div class="os-content">
<!-- Navigation -->
<nav class="nav-bar">
<a href="/ouroboros" class="nav-logo">← AAMOS</a>
<span class="nav-title">Bolagsonboarding</span>
<span class="nav-badge">BETA</span>
</nav>
<!-- Hero -->
<div class="hero">
<div class="hero-eyebrow">AAMOS · OUROBOROS</div>
<h1 class="hero-title">Starta ett bolag<br>på 15 minuter.</h1>
<p class="hero-sub">Automatisk provisioning, bankintegration, skatteregistrering och checklista — på direkten.</p>
<div class="hero-badge">⚡ Powered by AI · Bolagsverket · GECL</div>
</div>
<!-- Main content -->
<div class="container">
<!-- Step indicator -->
<div class="steps-bar">
<div class="step-dot active" id="dot1">1</div>
<div class="step-line" id="line1"></div>
<div class="step-dot" id="dot2">2</div>
<div class="step-line" id="line2"></div>
<div class="step-dot" id="dot3">3</div>
<div class="step-line" id="line3"></div>
<div class="step-dot" id="dot4">4</div>
</div>
<!-- ── STEP 1: Org-nummer ── -->
<div class="step-view active" id="step1">
<div class="section-header">Vilket bolag gäller det?</div>
<p class="section-sub">Ange organisationsnummer — vi hämtar resten från Bolagsverket automatiskt.</p>
<div class="card fade-in">
<div class="card-header">
<div class="card-icon blue">🏢</div>
<div>
<div class="card-title">Organisationsnummer</div>
<div class="card-sub">10 siffror, format XXXXXX-XXXX</div>
</div>
</div>
<div class="card-body">
<div class="field-group">
<div class="field-label">
Org-nummer <span class="field-required">*</span>
</div>
<input
type="text"
id="orgNrInput"
class="field-input"
placeholder="556000-0000"
maxlength="11"
autocomplete="off"
autocorrect="off"
spellcheck="false"
>
<div class="field-hint" id="orgNrHint">Ange bolagets registrerade organisationsnummer</div>
</div>
<!-- Company preview -->
<div class="company-preview" id="companyPreview">
<div class="flex items-center justify-between">
<div class="company-preview-name" id="previewName"></div>
<div class="company-preview-badge" id="previewStatus">✓ FOUND</div>
</div>
<div class="company-preview-row" id="previewAddress"></div>
<div class="company-preview-row">
<span id="previewLegalForm"></span>
&nbsp;·&nbsp;
<span id="previewSni"></span>
</div>
</div>
<div class="mt-16">
<button class="btn-primary" id="step1Next" disabled onclick="goStep2()">
Fortsätt →
</button>
</div>
</div>
</div>
</div>
<!-- ── STEP 2: Välj plan & kontakt ── -->
<div class="step-view" id="step2">
<div class="section-header">Välj plan</div>
<p class="section-sub">Alla planer inkluderar automatisk skatteregistrering och bankintegration.</p>
<div class="card fade-in">
<div class="card-header">
<div class="card-icon purple"></div>
<div>
<div class="card-title">Abonnemangsplan</div>
<div class="card-sub">Kan ändras när som helst</div>
</div>
</div>
<div class="card-body">
<div class="plans-grid">
<div class="plan-card" onclick="selectPlan('starter', this)" data-plan="starter">
<div class="plan-check"></div>
<div class="plan-emoji">🌱</div>
<div class="plan-name">Starter</div>
<div class="plan-price">990 kr/mån</div>
</div>
<div class="plan-card selected" onclick="selectPlan('professional', this)" data-plan="professional">
<div class="plan-check"></div>
<div class="plan-emoji">🚀</div>
<div class="plan-name">Professional</div>
<div class="plan-price">2 990 kr/mån</div>
</div>
<div class="plan-card" onclick="selectPlan('enterprise', this)" data-plan="enterprise">
<div class="plan-check"></div>
<div class="plan-emoji">🏛️</div>
<div class="plan-name">Enterprise</div>
<div class="plan-price">9 900 kr/mån</div>
</div>
</div>
<div class="divider mt-12"></div>
<div class="field-group mt-12">
<div class="field-label">Admin-namn <span class="field-required">*</span></div>
<input type="text" id="adminName" class="field-input" placeholder="Anna Andersson" autocomplete="name">
</div>
<div class="field-group">
<div class="field-label">E-postadress <span class="field-required">*</span></div>
<input type="email" id="adminEmail" class="field-input" placeholder="anna@bolaget.se" autocomplete="email">
<div class="field-hint">Välkomstmail och dashboard-länk skickas hit</div>
</div>
<div class="field-group">
<div class="field-label">Lösenord <span class="field-required">*</span></div>
<input type="password" id="adminPassword" class="field-input" placeholder="Minst 8 tecken" autocomplete="new-password" minlength="8">
<div class="field-hint">Minst 8 tecken — används för att logga in i dashboard</div>
</div>
<div class="flex gap-8 mt-16">
<button class="btn-secondary" onclick="goStep1()" style="flex:1">← Tillbaka</button>
<button class="btn-primary" onclick="goStep3()" style="flex:2">Starta provisioning →</button>
</div>
</div>
</div>
</div>
<!-- ── STEP 3: Provisioning progress ── -->
<div class="step-view" id="step3">
<div class="section-header">Sätter upp bolaget…</div>
<p class="section-sub" id="step3Sub">AAMOS konfigurerar allt automatiskt — brukar ta under 15 sekunder.</p>
<div class="card fade-in">
<div class="card-header">
<div class="card-icon blue">⚙️</div>
<div>
<div class="card-title">Automatisk provisioning</div>
<div class="card-sub" id="progressLabel">Initierar…</div>
</div>
</div>
<div class="card-body">
<div class="progress-wrap">
<div class="progress-bar" id="progressBar" style="width:0%"></div>
</div>
<div class="field-hint text-center" id="progressPct">0% klart</div>
<div class="step-items mt-12" id="provSteps">
<!-- Injected by JS -->
</div>
</div>
</div>
</div>
<!-- ── STEP 4: Klar! Dashboard + Checklista ── -->
<div class="step-view" id="step4">
<div class="card fade-in">
<div class="success-hero">
<div class="success-icon"></div>
<div class="success-title" id="successTitle">Klart!</div>
<div class="success-sub" id="successSub">Bolaget är nu konfigurerat i AAMOS.<br>Välkomstmail skickat.</div>
</div>
</div>
<!-- Stats -->
<div class="stats-row fade-in">
<div class="stat-card">
<div class="stat-value" id="statSecs"></div>
<div class="stat-label">Sekunder</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statModules"></div>
<div class="stat-label">Moduler</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statItems"></div>
<div class="stat-label">Åtgärdspunkter</div>
</div>
</div>
<!-- Onboarding checklist -->
<div class="card fade-in" id="checklistCard">
<div class="card-header">
<div class="card-icon green">📋</div>
<div>
<div class="card-title">Onboarding-checklista</div>
<div class="card-sub" id="checklistMeta">Laddar…</div>
</div>
</div>
<div class="card-body" id="checklistBody">
<!-- Injected by JS -->
</div>
</div>
<!-- Dashboard link -->
<div class="card fade-in mt-16">
<div class="card-body text-center">
<div style="font-size:32px; margin-bottom:12px;">🔐</div>
<div style="font-size:17px; font-weight:600; margin-bottom:6px;">Klart att köra!</div>
<div style="font-size:14px; color: var(--ios-label-2); margin-bottom:12px;">
Ditt AAMOS-konto är aktiverat med alla moduler konfigurerade.
</div>
<div id="credentialBox" style="background:var(--ios-fill);border-radius:var(--ios-corner-med);padding:14px 16px;margin-bottom:16px;text-align:left;font-size:14px;line-height:1.7;"></div>
<a id="dashboardBtn" href="/ouroboros/app/login.html" class="btn-primary" style="display:inline-block; text-decoration:none; padding: 14px 24px;">
Klart! Logga in här →
</a>
</div>
</div>
<!-- Restart -->
<div class="text-center mt-16">
<button class="btn-secondary" onclick="restart()">Onboarda ett till bolag</button>
</div>
</div>
</div><!-- /container -->
<script>
// ── State ────────────────────────────────────────────────────────────────────
let state = {
orgNr: null,
companyData: null,
plan: 'professional',
adminEmail: null,
adminName: null,
adminPassword: null,
jobId: null,
jobData: null,
};
const PROV_STEPS = [
{ label: 'Hämtar bolagsdata från Bolagsverket', icon: '🏛️' },
{ label: 'Skapar organisation i Stammregister', icon: '📋' },
{ label: 'Konfigurerar standardroller', icon: '👥' },
{ label: 'Tillämpar SNI-baserat regelset', icon: '⚖️' },
{ label: 'Aktiverar moduler', icon: '🔧' },
{ label: 'Genererar onboarding-checklista', icon: '✅' },
{ label: 'Skickar välkomstmail', icon: '📧' },
{ label: 'GECL-signerar provisioning-record', icon: '🔐' },
];
// ── DOM helpers ──────────────────────────────────────────────────────────────
const $ = id => document.getElementById(id);
const setActive = (n) => {
[1,2,3,4].forEach(i => {
$(`step${i}`).classList.toggle('active', i === n);
const dot = $(`dot${i}`);
dot.classList.toggle('active', i === n);
dot.classList.toggle('done', i < n);
if (i < 4) $(`line${i}`).classList.toggle('done', i < n);
});
window.scrollTo({ top: 0, behavior: 'smooth' });
};
// ── Org-nr input handling ────────────────────────────────────────────────────
let lookupTimeout;
$('orgNrInput').addEventListener('input', function() {
clearTimeout(lookupTimeout);
let v = this.value.replace(/[^\d-]/g, '');
// Auto-format
const digits = v.replace(/-/g, '');
if (digits.length > 6 && !v.includes('-')) {
v = digits.slice(0,6) + '-' + digits.slice(6,10);
}
this.value = v;
$('step1Next').disabled = true;
$('companyPreview').classList.remove('show');
const clean = v.replace(/[-\s]/g, '');
if (clean.length === 10) {
this.classList.remove('valid', 'error');
$('orgNrHint').textContent = 'Slår upp bolag…';
lookupTimeout = setTimeout(() => lookupOrgNr(clean), 500);
} else {
$('orgNrHint').textContent = 'Ange 10 siffror (XXXXXX-XXXX)';
}
});
async function lookupOrgNr(orgNr) {
try {
const r = await fetch(`/api/company/lookup/${orgNr}`);
const d = await r.json();
state.orgNr = orgNr;
state.companyData = d;
if (d.status === 'FOUND' || d.status === 'PARTIAL') {
$('orgNrInput').classList.add('valid');
$('orgNrHint').textContent = d.status === 'FOUND'
? '✅ Bolag verifierat via Bolagsverket'
: '⚠️ Delvis data — ange resterande uppgifter manuellt';
$('previewName').textContent = d.name || `Org.nr ${d.org_nr_formatted}`;
$('previewStatus').textContent = d.status === 'FOUND' ? '✓ VERIFIERAD' : '~ PARTIAL';
$('previewStatus').style.background = d.status === 'FOUND' ? 'rgba(52,199,89,0.15)' : 'rgba(255,149,0,0.15)';
$('previewAddress').textContent = [d.address?.street, d.address?.city].filter(Boolean).join(', ') || 'Adress saknas';
$('previewLegalForm').textContent = d.legal_form || 'Aktiebolag';
$('previewSni').textContent = d.sni_category || 'Okänd bransch';
$('companyPreview').classList.add('show');
$('step1Next').disabled = false;
} else {
$('orgNrInput').classList.add('error');
$('orgNrHint').textContent = `${d.error || 'Bolaget hittades inte'}`;
}
} catch (err) {
$('orgNrHint').textContent = '⚠️ Kunde inte nå Bolagsverket — fortsätt ändå';
$('step1Next').disabled = false;
state.orgNr = orgNr;
}
}
// ── Plan selector ────────────────────────────────────────────────────────────
function selectPlan(plan, el) {
state.plan = plan;
document.querySelectorAll('.plan-card').forEach(c => c.classList.remove('selected'));
el.classList.add('selected');
}
// ── Navigation ───────────────────────────────────────────────────────────────
function goStep1() { setActive(1); }
function goStep2() { setActive(2); }
function goStep3() {
const name = $('adminName').value.trim();
const email = $('adminEmail').value.trim();
const password = $('adminPassword').value;
if (!name || !email) {
alert('Ange namn och e-postadress.');
return;
}
if (!password || password.length < 8) {
alert('Lösenordet måste vara minst 8 tecken.');
return;
}
state.adminName = name;
state.adminEmail = email;
state.adminPassword = password;
setActive(3);
startProvisioning();
}
// ── Provisioning ─────────────────────────────────────────────────────────────
function renderProvSteps(doneCount) {
const container = $('provSteps');
container.innerHTML = PROV_STEPS.map((s, i) => {
const isDone = i < doneCount;
const isRunning = i === doneCount;
let iconClass = 'pending', iconContent = '○';
if (isDone) { iconClass = 'done'; iconContent = '✓'; }
if (isRunning) { iconClass = 'running'; iconContent = '<span class="spinner">⟳</span>'; }
return `
<div class="step-item">
<div class="step-status-icon ${iconClass}">${iconContent}</div>
<div class="step-item-text">
<div class="step-item-label">${s.label}</div>
${isRunning ? '<div class="step-item-sub">Pågår…</div>' :
isDone ? '<div class="step-item-sub" style="color:var(--ios-green)">Klart</div>' : ''}
</div>
<div style="font-size:18px">${s.icon}</div>
</div>`;
}).join('');
}
async function startProvisioning() {
renderProvSteps(0);
updateProgress(0);
try {
const resp = await fetch('/api/company/provision', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
org_nr: state.orgNr,
plan: state.plan,
admin_email: state.adminEmail,
admin_name: state.adminName,
admin_password: state.adminPassword,
}),
});
const job = await resp.json();
state.jobId = job.job_id;
pollJob(state.jobId);
} catch (err) {
$('step3Sub').textContent = `Fel: ${err.message}`;
}
}
function updateProgress(pct) {
$('progressBar').style.width = pct + '%';
$('progressPct').textContent = pct + '% klart';
$('progressLabel').textContent = pct < 100 ? `${pct}% — pågår…` : 'Klart!';
}
let pollInterval;
function pollJob(jobId) {
let attempts = 0;
pollInterval = setInterval(async () => {
attempts++;
if (attempts > 120) { clearInterval(pollInterval); return; } // 2 min timeout
try {
const r = await fetch(`/api/company/provisioning-status/${jobId}`);
const d = await r.json();
if (d.status === 'running') {
const pct = d.progress_pct || 0;
updateProgress(pct);
renderProvSteps(d.steps_done || 0);
} else if (d.status === 'completed') {
clearInterval(pollInterval);
state.jobData = d;
updateProgress(100);
renderProvSteps(8);
setTimeout(() => goStep4(d), 800);
} else if (d.status === 'failed') {
clearInterval(pollInterval);
$('step3Sub').textContent = `Fel: ${d.error || 'Okänt fel'}`;
}
} catch (_) {}
}, 1000);
}
// ── Step 4: Result ────────────────────────────────────────────────────────────
function goStep4(data) {
setActive(4);
$('successTitle').textContent = `${data.company_name || 'Bolaget'} är redo! 🎉`;
$('successSub').innerHTML = `Konfigurerat på <strong>${data.elapsed_seconds}s</strong>. Välkomstmail skickat till ${data.welcome_email?.to || state.adminEmail}.`;
$('statSecs').textContent = data.elapsed_seconds + 's';
$('statModules').textContent = Object.keys(data.module_status || {}).length || '—';
$('statItems').textContent = data.checklist?.total_items || '—';
// Visa inloggningsuppgifter
const orgId = data.org_id || '—';
const maskPwd = state.adminPassword
? '•'.repeat(Math.min(state.adminPassword.length, 12))
: '••••••••';
$('credentialBox').innerHTML = [
'<div style="margin-bottom:8px;font-weight:700;font-size:12px;color:var(--ios-label-2);text-transform:uppercase;letter-spacing:.5px">Inloggningsuppgifter</div>',
`<div>🏢 Org-ID: <strong style="font-family:monospace;font-size:13px">${orgId}</strong></div>`,
`<div>📧 E-post: <strong>${state.adminEmail}</strong></div>`,
`<div>🔑 Lösenord: <strong>${maskPwd}</strong> (ditt valda lösenord)</div>`,
].join('');
// Sätt login-URL
$('dashboardBtn').href = data.dashboard_url || '/ouroboros/app/login.html';
if (data.checklist) {
renderChecklist(data.checklist);
}
}
function renderChecklist(cl) {
$('checklistMeta').textContent = `${cl.total_items} åtgärder · Beräknad tid ~${cl.estimated_hours}h`;
const body = $('checklistBody');
body.innerHTML = cl.categories.map(cat => `
<div class="checklist-category">
<div class="checklist-cat-header">
<span>${cat.icon}</span> ${cat.category}
<span class="priority-badge priority-${cat.priority}">${cat.priority}</span>
</div>
${cat.items.map(item => `
<div class="checklist-item ${item.status === 'auto' ? 'auto' : ''}" onclick="toggleCheck(this)">
<div class="checklist-checkbox">${item.status === 'auto' ? '<span style="color:#fff;font-size:11px;">✓</span>' : ''}</div>
<div style="flex:1">
<div class="checklist-title">${item.title}</div>
<div class="checklist-meta">
<span class="checklist-time">⏱ ${item.estimated_min} min</span>
${item.note ? `<span class="tag tag-blue">${item.note}</span>` : ''}
</div>
</div>
</div>
`).join('')}
</div>
`).join('<div class="divider"></div>');
}
function toggleCheck(el) {
if (el.classList.contains('auto')) return;
el.classList.toggle('completed');
const cb = el.querySelector('.checklist-checkbox');
cb.innerHTML = el.classList.contains('completed')
? '<span style="color:#fff;font-size:11px;">✓</span>'
: '';
}
function restart() {
clearInterval(pollInterval);
state = { orgNr: null, companyData: null, plan: 'professional', adminEmail: null, adminName: null, adminPassword: null, jobId: null, jobData: null };
$('orgNrInput').value = '';
$('orgNrInput').className = 'field-input';
$('companyPreview').classList.remove('show');
$('orgNrHint').textContent = 'Ange bolagets registrerade organisationsnummer';
$('step1Next').disabled = true;
$('adminName').value = '';
$('adminEmail').value = '';
setActive(1);
}
// Init: pre-select professional plan
document.querySelector('[data-plan="professional"]').classList.add('selected');
// Handle ?org= querystring
const params = new URLSearchParams(location.search);
if (params.get('org')) {
// Already provisioned — show success hint
const orgHint = document.createElement('div');
orgHint.style.cssText = 'background:rgba(52,199,89,0.1);border:1px solid rgba(52,199,89,0.3);border-radius:12px;padding:16px;margin:16px 0;text-align:center;font-size:14px;color:#1a7a30;font-weight:500;';
orgHint.innerHTML = `✅ Org <code style="font-family:monospace">${params.get('org')}</code> är redan provisionerat i AMOS.`;
document.querySelector('.container').prepend(orgHint);
}
</script>
</div><!-- /os-content -->
</div><!-- /os-main -->
</div><!-- /os-layout -->
</body>
</html>