diff --git a/package.json b/package.json
index 602e8af..1b8c132 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,9 @@
"main": "server.js",
"scripts": {
"start": "node server.js",
- "dev": "node server.js"
+ "dev": "node server.js",
+ "build:css": "sass src/styles/main.scss public/css/main.css --no-source-map --style=expanded",
+ "watch:css": "sass --watch src/styles/main.scss public/css/main.css --no-source-map --style=expanded"
},
"dependencies": {
"express": "^4.18.2",
diff --git a/public/css/main.css b/public/css/main.css
index 23b658a..64a2b79 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -1 +1,979 @@
-*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html{font-size:16px;scroll-behavior:smooth}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans KR",sans-serif;background:#f3f4f6;color:#111827;line-height:1.6;min-height:100vh}#app{display:flex;flex-direction:column;min-height:100vh}#app-header{background:#fff;border-bottom:1px solid #e5e7eb;padding:0 1.5rem;position:sticky;top:0;z-index:100;box-shadow:0 1px 8px rgba(0,0,0,.06)}.header-inner{max-width:760px;margin:0 auto;display:flex;align-items:center;gap:1rem;height:56px}.header-title{font-size:.85rem;font-weight:600;color:#2563eb;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.lang-btn{font-size:.78rem;padding:.3rem .8rem;border:1px solid #e5e7eb;border-radius:20px;background:rgba(0,0,0,0);cursor:pointer;color:#6b7280;transition:all .15s;white-space:nowrap}.lang-btn:hover{border-color:#2563eb;color:#2563eb}#app-main{flex:1;padding:2rem 1rem 4rem}.step-wrap{max-width:720px;margin:0 auto}.step-progress{display:flex;align-items:center;gap:0;margin-bottom:2rem}.step-progress .step-dot{display:flex;flex-direction:column;align-items:center;gap:.25rem;flex:1;position:relative}.step-progress .step-dot:not(:last-child)::after{content:"";position:absolute;top:14px;left:calc(50% + 14px);right:calc(-50% + 14px);height:2px;background:#e5e7eb;transition:background .3s}.step-progress .step-dot.done::after{background:#2563eb}.step-progress .step-dot .dot-circle{width:28px;height:28px;border-radius:50%;border:2px solid #e5e7eb;background:#fff;display:flex;align-items:center;justify-content:center;font-size:.72rem;font-weight:700;color:#6b7280;transition:all .3s;position:relative;z-index:1}.step-progress .step-dot .dot-label{font-size:.68rem;color:#6b7280;text-align:center;line-height:1.2}.step-progress .step-dot.done .dot-circle{background:#2563eb;border-color:#2563eb;color:#fff}.step-progress .step-dot.active .dot-circle{background:#2563eb;border-color:#2563eb;color:#fff;box-shadow:0 0 0 4px rgba(37,99,235,.2)}.step-progress .step-dot.active .dot-label{color:#2563eb;font-weight:600}.card{background:#fff;border-radius:12px;box-shadow:0 1px 4px rgba(0,0,0,.1),0 4px 16px rgba(0,0,0,.06);padding:2rem;margin-bottom:1.5rem}.card.card-sm{padding:1.5rem}.section-title{font-size:1.3rem;font-weight:700;margin-bottom:.4rem}.section-subtitle{color:#6b7280;font-size:.9rem;margin-bottom:1.5rem;line-height:1.5}.form-group{margin-bottom:1.2rem}.form-group label{display:block;font-size:.875rem;font-weight:600;margin-bottom:.5rem;color:#374151}.form-group input[type=text]{width:100%;padding:.6rem .9rem;border:1.5px solid #e5e7eb;border-radius:8px;font-size:.9rem;outline:none;transition:border-color .2s}.form-group input[type=text]:focus{border-color:#2563eb}.form-group input[type=text]::placeholder{color:#9ca3af}.radio-group{display:flex;flex-wrap:wrap;gap:.6rem}.radio-group label{display:flex;align-items:center;gap:.4rem;padding:.5rem 1rem;border:1.5px solid #e5e7eb;border-radius:24px;cursor:pointer;font-size:.875rem;font-weight:normal;color:#374151;transition:all .15s;margin:0;white-space:nowrap}.radio-group label input[type=radio]{display:none}.radio-group label:hover{border-color:#2563eb;color:#2563eb}.radio-group label.selected{border-color:#2563eb;background:rgba(37,99,235,.07);color:#2563eb;font-weight:600}.notice-box{background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;padding:1rem 1.2rem;margin-bottom:1.5rem}.notice-box p{font-size:.85rem;color:#1e40af;line-height:1.6}.notice-box p::before{content:"• "}.notice-box p+p{margin-top:.3rem}.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;padding:.65rem 1.6rem;border-radius:8px;border:none;font-size:.9rem;font-weight:600;cursor:pointer;transition:all .15s;text-decoration:none}.btn:disabled{opacity:.45;cursor:not-allowed}.btn.btn-primary{background:#2563eb;color:#fff}.btn.btn-primary:not(:disabled):hover{background:#1d4ed8;transform:translateY(-1px);box-shadow:0 4px 12px rgba(37,99,235,.35)}.btn.btn-outline{background:rgba(0,0,0,0);color:#2563eb;border:1.5px solid #2563eb}.btn.btn-outline:hover{background:rgba(37,99,235,.06)}.btn.btn-gray{background:#f3f4f6;color:#374151;border:1.5px solid #e5e7eb}.btn.btn-gray:not(:disabled):hover{background:#e5e7eb}.btn.btn-lg{padding:.85rem 2.2rem;font-size:1rem;border-radius:10px}.btn.btn-full{width:100%}.btn.btn-select{background:#2563eb;color:#fff;flex:1;font-size:1rem;padding:.9rem 1rem}.btn.btn-select:not(:disabled):hover{background:#1d4ed8}.btn.btn-reject{background:#f9fafb;color:#6b7280;border:1.5px solid #e5e7eb;flex:1;font-size:1rem;padding:.9rem 1rem}.btn.btn-reject:not(:disabled):hover{background:#f3f4f6;color:#374151}.btn.btn-selected-select{background:#16a34a;color:#fff;flex:1;padding:.9rem 1rem;font-size:1rem}.btn.btn-selected-reject{background:#dc2626;color:#fff;flex:1;padding:.9rem 1rem;font-size:1rem}.btn-row{display:flex;gap:.75rem;margin-top:1.2rem}.btn-row.center{justify-content:center}.btn-row.end{justify-content:flex-end}.progress-bar-wrap{margin-bottom:1.5rem}.progress-bar-wrap .progress-label{display:flex;justify-content:space-between;font-size:.82rem;color:#6b7280;margin-bottom:.4rem;font-weight:600}.progress-bar-wrap .progress-track{height:6px;background:#e5e7eb;border-radius:3px;overflow:hidden}.progress-bar-wrap .progress-track .progress-fill{height:100%;background:#2563eb;border-radius:3px;transition:width .4s ease}.case-card .case-header{display:flex;align-items:center;gap:.75rem;margin-bottom:1.4rem;padding-bottom:1rem;border-bottom:1px solid #e5e7eb}.case-card .case-header .team-badge{font-size:1.4rem;font-weight:800;color:#2563eb;background:rgba(37,99,235,.08);border-radius:8px;padding:.3rem .8rem}.case-card .case-header .industry-tag{font-size:.82rem;background:#f3f4f6;border:1px solid #e5e7eb;border-radius:20px;padding:.25rem .75rem;color:#6b7280;font-weight:500}.var-row{display:flex;align-items:center;gap:.75rem;margin-bottom:.85rem}.var-row .var-name{font-size:.82rem;font-weight:600;color:#374151;min-width:110px;flex-shrink:0}.var-row .var-bar-track{flex:1;height:10px;background:#f3f4f6;border-radius:5px;overflow:hidden}.var-row .var-bar-track .var-bar-fill{height:100%;border-radius:5px;transition:width .5s ease}.var-row .var-score{font-size:.85rem;font-weight:700;min-width:32px;text-align:right;color:#111827}.var-bar-fill[data-level=low]{background:linear-gradient(90deg, #EF4444, #F87171)}.var-bar-fill[data-level=mid]{background:linear-gradient(90deg, #FBBF24, #FCD34D)}.var-bar-fill[data-level=high]{background:linear-gradient(90deg, #22C55E, #4ADE80)}.judgment-section{margin-top:1.5rem;padding-top:1.2rem;border-top:1px solid #e5e7eb}.judgment-section .judgment-label{font-size:.875rem;font-weight:600;margin-bottom:.75rem;color:#374151}.confidence-section{margin-top:1.2rem;padding:1rem 1.2rem;background:#f9fafb;border-radius:8px;border:1px solid #e5e7eb}.confidence-section .confidence-label{font-size:.82rem;font-weight:600;color:#374151;margin-bottom:.6rem;display:flex;justify-content:space-between}.confidence-section .confidence-slider{width:100%;appearance:none;height:6px;border-radius:3px;background:#e5e7eb;outline:none;cursor:pointer}.confidence-section .confidence-slider::-webkit-slider-thumb{appearance:none;width:20px;height:20px;border-radius:50%;background:#2563eb;cursor:pointer;border:2px solid #fff;box-shadow:0 1px 4px rgba(0,0,0,.2)}.confidence-section .confidence-labels{display:flex;justify-content:space-between;margin-top:.4rem}.confidence-section .confidence-labels .cl{font-size:.7rem;color:#6b7280}.judgment-likert-row{display:flex;gap:.5rem;margin-top:.5rem}.judgment-likert-btn{flex:1;display:flex;flex-direction:column;align-items:center;gap:.3rem;padding:.7rem .3rem;border:2px solid #e5e7eb;border-radius:8px;background:#fff;cursor:pointer;transition:all .15s}.judgment-likert-btn .jl-num{font-size:1.1rem;font-weight:800;color:#6b7280}.judgment-likert-btn .jl-label{font-size:.65rem;color:#6b7280;text-align:center;line-height:1.3}.judgment-likert-btn:hover{border-color:#2563eb}.judgment-likert-btn.active{border-color:#2563eb;background:rgba(37,99,235,.07)}.judgment-likert-btn.active .jl-num{color:#2563eb}.judgment-likert-btn.active .jl-label{color:#2563eb}.judgment-likert-btn[data-val="1"].active{border-color:#dc2626;background:rgba(220,38,38,.07)}.judgment-likert-btn[data-val="1"].active .jl-num,.judgment-likert-btn[data-val="1"].active .jl-label{color:#dc2626}.judgment-likert-btn[data-val="2"].active{border-color:#f97316;background:rgba(249,115,22,.07)}.judgment-likert-btn[data-val="2"].active .jl-num,.judgment-likert-btn[data-val="2"].active .jl-label{color:#f97316}.judgment-likert-btn[data-val="3"].active{border-color:#d97706;background:rgba(217,119,6,.07)}.judgment-likert-btn[data-val="3"].active .jl-num,.judgment-likert-btn[data-val="3"].active .jl-label{color:#d97706}.judgment-likert-btn[data-val="4"].active{border-color:#22c55e;background:rgba(34,197,94,.07)}.judgment-likert-btn[data-val="4"].active .jl-num,.judgment-likert-btn[data-val="4"].active .jl-label{color:#22c55e}.judgment-likert-btn[data-val="5"].active{border-color:#16a34a;background:rgba(22,163,74,.07)}.judgment-likert-btn[data-val="5"].active .jl-num,.judgment-likert-btn[data-val="5"].active .jl-label{color:#16a34a}.ahp-pair-card .pair-title{font-size:.85rem;color:#6b7280;margin-bottom:1.2rem;font-weight:500}.ahp-pair-card .pair-criteria{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:1.2rem}.ahp-pair-card .pair-criteria .crit{flex:1;text-align:center;font-size:1rem;font-weight:700;color:#111827;padding:.6rem;border-radius:8px;transition:background .2s}.ahp-pair-card .pair-criteria .crit.crit-left{text-align:right}.ahp-pair-card .pair-criteria .crit.crit-right{text-align:left}.ahp-pair-card .pair-criteria .crit.crit-active-left{color:#2563eb;background:rgba(37,99,235,.07)}.ahp-pair-card .pair-criteria .crit.crit-active-right{color:#16a34a;background:rgba(22,163,74,.07)}.ahp-pair-card .pair-criteria .vs-badge{font-size:.8rem;font-weight:700;color:#6b7280;background:#f3f4f6;border:1px solid #e5e7eb;border-radius:20px;padding:.25rem .6rem;flex-shrink:0}.ahp-slider-wrap{padding:.5rem 0}.ahp-scale-labels{display:flex;justify-content:space-between;margin-bottom:.5rem;padding:0 12px}.ahp-scale-labels span{font-size:.7rem;color:#6b7280;font-weight:600;text-align:center;min-width:0}.ahp-scale-labels .sc-mid{font-weight:800;color:#374151;font-size:.75rem}.ahp-slider{width:100%;appearance:none;height:8px;border-radius:4px;background:linear-gradient(to right, rgba(37, 99, 235, 0.15) 0%, #2563EB 50%, rgba(22, 163, 74, 0.15) 100%);outline:none;cursor:pointer}.ahp-slider::-webkit-slider-thumb{appearance:none;width:24px;height:24px;border-radius:50%;background:#fff;border:3px solid #2563eb;box-shadow:0 2px 6px rgba(0,0,0,.2);cursor:pointer;transition:border-color .2s}.ahp-description{text-align:center;margin-top:1rem;padding:.7rem 1rem;background:#f0f9ff;border:1px solid #bae6fd;border-radius:8px;font-size:.875rem;font-weight:600;color:#0369a1;min-height:42px}.cr-warning{background:#fef9c3;border:1px solid #fde047;border-radius:8px;padding:1rem 1.2rem;margin-top:1rem}.cr-warning p{font-size:.875rem;color:#713f12;margin-bottom:.75rem;font-weight:600}.weight-chart{margin-top:.5rem}.weight-chart .wc-row{display:flex;align-items:center;gap:.75rem;margin-bottom:.7rem}.weight-chart .wc-row .wc-label{font-size:.82rem;font-weight:600;color:#374151;min-width:110px;flex-shrink:0}.weight-chart .wc-row .wc-track{flex:1;height:18px;background:#f3f4f6;border-radius:9px;overflow:hidden}.weight-chart .wc-row .wc-track .wc-fill{height:100%;background:linear-gradient(90deg, #2563EB, #60A5FA);border-radius:9px;transition:width .6s ease}.weight-chart .wc-row .wc-pct{font-size:.82rem;font-weight:700;color:#2563eb;min-width:38px;text-align:right}.stats-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(160px, 1fr));gap:1rem;margin-top:1rem}.stats-grid .stat-box{background:#f3f4f6;border:1px solid #e5e7eb;border-radius:8px;padding:1rem;text-align:center}.stats-grid .stat-box .stat-val{font-size:1.6rem;font-weight:800;color:#2563eb}.stats-grid .stat-box .stat-label{font-size:.75rem;color:#6b7280;margin-top:.25rem;line-height:1.3}.stats-grid .stat-box.stat-warn .stat-val{color:#d97706}.stats-grid .stat-box.stat-ok .stat-val{color:#16a34a}.comparison-table-wrap{overflow-x:auto;margin-top:1rem;border-radius:8px;border:1px solid #e5e7eb}.comparison-table-wrap table{width:100%;border-collapse:collapse;font-size:.82rem}.comparison-table-wrap table th{background:#f9fafb;padding:.6rem .8rem;text-align:center;font-weight:700;border-bottom:1px solid #e5e7eb;white-space:nowrap;color:#374151}.comparison-table-wrap table td{padding:.55rem .8rem;text-align:center;border-bottom:1px solid #e5e7eb}.comparison-table-wrap table td:first-child{font-weight:700}.comparison-table-wrap table tr:last-child td{border-bottom:none}.comparison-table-wrap table tr.mismatch td{background:#fef2f2;color:#dc2626}.comparison-table-wrap table tr.mismatch td:first-child{color:#dc2626}.badge{display:inline-block;padding:.2rem .55rem;border-radius:12px;font-size:.75rem;font-weight:700;line-height:1}.badge.badge-select{background:#dbeafe;color:#2563eb}.badge.badge-reject{background:#f3f4f6;color:#6b7280}.badge.badge-match{background:#dcfce7;color:#16a34a}.badge.badge-mismatch{background:#fee2e2;color:#dc2626}.badge.badge-neutral{background:#fef9c3;color:#d97706}.neutral-row td{background:#fafaf0;color:#d97706}.likert-group{margin-bottom:1.5rem}.likert-group .likert-q{font-size:.9rem;font-weight:600;color:#111827;margin-bottom:.75rem;line-height:1.5}.likert-group .likert-options{display:flex;gap:.5rem}.likert-group .likert-options label{flex:1;display:flex;flex-direction:column;align-items:center;gap:.3rem;cursor:pointer}.likert-group .likert-options label input[type=radio]{display:none}.likert-group .likert-options label .likert-btn{width:100%;padding:.55rem .2rem;border:2px solid #e5e7eb;border-radius:8px;text-align:center;font-size:.9rem;font-weight:700;color:#6b7280;transition:all .15s;background:#fff}.likert-group .likert-options label .likert-desc{font-size:.65rem;color:#6b7280;text-align:center;line-height:1.2}.likert-group .likert-options label input:checked+.likert-btn{border-color:#2563eb;background:rgba(37,99,235,.08);color:#2563eb}.likert-group .likert-options label:hover .likert-btn{border-color:#2563eb}.open-textarea{width:100%;padding:.8rem 1rem;border:1.5px solid #e5e7eb;border-radius:8px;font-size:.875rem;font-family:inherit;resize:vertical;min-height:100px;outline:none;transition:border-color .2s}.open-textarea:focus{border-color:#2563eb}.complete-screen{text-align:center;padding:2rem 1rem}.complete-screen .complete-icon{font-size:3.5rem;margin-bottom:1rem}.complete-screen .complete-title{font-size:1.5rem;font-weight:800;margin-bottom:.5rem}.complete-screen .complete-desc{color:#6b7280;font-size:.9rem;margin-bottom:2rem;line-height:1.7}.json-output{background:#1e293b;color:#94a3b8;border-radius:8px;padding:1.2rem;font-family:"Menlo","Monaco","Consolas",monospace;font-size:.75rem;line-height:1.6;text-align:left;overflow-x:auto;max-height:400px;overflow-y:auto;white-space:pre;margin-top:1rem}.divider{height:1px;background:#e5e7eb;margin:1.5rem 0}.text-muted{color:#6b7280;font-size:.85rem}.text-center{text-align:center}.mt-1{margin-top:.5rem}.mt-2{margin-top:1rem}.mt-3{margin-top:1.5rem}.mb-1{margin-bottom:.5rem}.highlight-text{color:#2563eb;font-weight:700}@media(max-width: 600px){#app-header{padding:0 1rem}.header-title{font-size:.78rem}.card{padding:1.2rem}.step-progress .step-dot .dot-label{display:none}.var-row .var-name{min-width:80px;font-size:.76rem}.ahp-pair-card .pair-criteria{flex-direction:column;text-align:center;gap:.5rem}.ahp-pair-card .pair-criteria .crit{text-align:center !important}.stats-grid{grid-template-columns:repeat(2, 1fr)}.comparison-table-wrap table{font-size:.75rem}.comparison-table-wrap table td,.comparison-table-wrap table th{padding:.4rem .5rem}.likert-group .likert-options label .likert-desc{display:none}.btn.btn-lg{padding:.75rem 1.5rem;font-size:.9rem}.btn-row.judgment-btns{gap:.5rem}}@media(max-width: 380px){.step-progress .step-dot .dot-circle{width:22px;height:22px;font-size:.65rem}}
\ No newline at end of file
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ scroll-behavior: smooth;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans KR", sans-serif;
+ background: #FFFFFF;
+ color: #111111;
+ line-height: 1.6;
+ min-height: 100vh;
+ -webkit-font-smoothing: antialiased;
+}
+
+:focus,
+:focus-visible {
+ outline: none;
+}
+
+#app {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
+#app-header {
+ background: #FFFFFF;
+ border-bottom: 1px solid #111111;
+ padding: 0 1.5rem;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+}
+
+.header-inner {
+ max-width: 760px;
+ margin: 0 auto;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ height: 56px;
+}
+
+.header-title {
+ font-size: 0.85rem;
+ font-weight: 700;
+ color: #111111;
+ flex: 1;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ letter-spacing: -0.01em;
+}
+
+.header-name {
+ direction: rtl;
+ font-size: 0.85rem;
+ font-weight: 700;
+ color: #111111;
+ flex: 1;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ letter-spacing: -0.01em;
+}
+
+#app-main {
+ flex: 1;
+ padding: 2rem 1rem 4rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.step-wrap {
+ width: 100%;
+ max-width: 720px;
+ margin: 0 auto;
+}
+
+.step-progress {
+ display: flex;
+ align-items: center;
+ gap: 0;
+ margin-bottom: 2rem;
+}
+.step-progress .step-dot {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.25rem;
+ flex: 1;
+ position: relative;
+}
+.step-progress .step-dot:not(:last-child)::after {
+ content: "";
+ position: absolute;
+ top: 14px;
+ left: calc(50% + 14px);
+ right: calc(-50% + 14px);
+ height: 1px;
+ background: #CCCCCC;
+}
+.step-progress .step-dot.done::after {
+ background: #111111;
+}
+.step-progress .step-dot .dot-circle {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ border: 1px solid #CCCCCC;
+ background: #FFFFFF;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.72rem;
+ font-weight: 700;
+ color: #6E6E6E;
+ position: relative;
+ z-index: 1;
+}
+.step-progress .step-dot .dot-label {
+ font-size: 0.68rem;
+ color: #6E6E6E;
+ text-align: center;
+ line-height: 1.2;
+}
+.step-progress .step-dot.done .dot-circle {
+ background: #111111;
+ border-color: #111111;
+ color: #FFFFFF;
+}
+.step-progress .step-dot.active .dot-circle {
+ background: #111111;
+ border-color: #111111;
+ color: #FFFFFF;
+}
+.step-progress .step-dot.active .dot-label {
+ color: #111111;
+ font-weight: 700;
+}
+
+.card {
+ background: #FFFFFF;
+ border: 1px solid #111111;
+ border-radius: 0;
+ padding: 2rem;
+ margin-bottom: 1.5rem;
+}
+.card.card-sm {
+ padding: 1.5rem;
+}
+
+.section-title {
+ font-size: 1.3rem;
+ font-weight: 800;
+ color: #111111;
+ letter-spacing: -0.02em;
+ margin-bottom: 0.4rem;
+}
+
+.section-subtitle {
+ color: #6E6E6E;
+ font-size: 0.9rem;
+ margin-bottom: 1.5rem;
+ line-height: 1.5;
+}
+
+.form-group {
+ margin-bottom: 1.2rem;
+}
+.form-group label {
+ display: block;
+ font-size: 0.875rem;
+ font-weight: 700;
+ margin-bottom: 0.5rem;
+ color: #111111;
+}
+.form-group input[type=text] {
+ width: 100%;
+ padding: 0.6rem 0.9rem;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ font-size: 0.9rem;
+ color: #111111;
+ background: #FFFFFF;
+}
+.form-group input[type=text]::placeholder {
+ color: #A3A3A3;
+}
+
+.radio-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+.radio-group label {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 0.5rem 1rem;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ cursor: pointer;
+ font-size: 0.875rem;
+ font-weight: 400;
+ color: #6E6E6E;
+ margin: 0;
+ white-space: nowrap;
+}
+.radio-group label input[type=radio] {
+ display: none;
+}
+.radio-group label:hover {
+ border-color: #111111;
+ color: #111111;
+}
+.radio-group label.selected {
+ border-color: #111111;
+ background: #111111;
+ color: #FFFFFF;
+ font-weight: 700;
+}
+
+.step-instruction {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ font-weight: 500;
+ color: #111111;
+ margin-bottom: 1.5rem;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.4rem;
+ padding: 0.65rem 1.6rem;
+ border-radius: 0;
+ border: 1px solid transparent;
+ font-size: 0.9rem;
+ font-weight: 700;
+ cursor: pointer;
+ text-decoration: none;
+ background: #FFFFFF;
+ color: #111111;
+}
+.btn:disabled {
+ opacity: 0.35;
+ cursor: not-allowed;
+}
+.btn.btn-primary {
+ background: #111111;
+ color: #FFFFFF;
+ border-color: #111111;
+}
+.btn.btn-primary:not(:disabled):hover {
+ background: #FFFFFF;
+ color: #111111;
+}
+.btn.btn-outline {
+ background: #FFFFFF;
+ color: #111111;
+ border-color: #111111;
+}
+.btn.btn-outline:not(:disabled):hover {
+ background: #111111;
+ color: #FFFFFF;
+}
+.btn.btn-gray {
+ background: #FFFFFF;
+ color: #6E6E6E;
+ border-color: #CCCCCC;
+}
+.btn.btn-gray:not(:disabled):hover {
+ border-color: #111111;
+ color: #111111;
+}
+.btn.btn-lg {
+ padding: 0.85rem 2.2rem;
+ font-size: 1rem;
+}
+.btn.btn-full {
+ width: 100%;
+}
+.btn.btn-select, .btn.btn-reject {
+ flex: 1;
+ font-size: 1rem;
+ padding: 0.9rem 1rem;
+ background: #FFFFFF;
+ color: #111111;
+ border-color: #CCCCCC;
+}
+.btn.btn-select:not(:disabled):hover, .btn.btn-reject:not(:disabled):hover {
+ border-color: #111111;
+}
+.btn.btn-selected-select, .btn.btn-selected-reject {
+ flex: 1;
+ padding: 0.9rem 1rem;
+ font-size: 1rem;
+ background: #111111;
+ color: #FFFFFF;
+ border-color: #111111;
+}
+
+.btn-row {
+ display: flex;
+ gap: 0.75rem;
+ margin-top: 1.2rem;
+}
+.btn-row.center {
+ justify-content: center;
+}
+.btn-row.end {
+ justify-content: flex-end;
+}
+
+.progress-bar-wrap {
+ margin-bottom: 1.5rem;
+}
+.progress-bar-wrap .progress-label {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.82rem;
+ color: #6E6E6E;
+ margin-bottom: 0.4rem;
+ font-weight: 700;
+}
+.progress-bar-wrap .progress-track {
+ height: 4px;
+ background: #E5E5E5;
+ border-radius: 0;
+ overflow: hidden;
+}
+.progress-bar-wrap .progress-track .progress-fill {
+ height: 100%;
+ background: #111111;
+ transition: width 0.3s ease;
+}
+
+.case-card .case-header {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-bottom: 1.4rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid #CCCCCC;
+}
+.case-card .case-header .team-badge {
+ font-size: 1.4rem;
+ font-weight: 800;
+ color: #111111;
+ background: #FFFFFF;
+ border: 1px solid #111111;
+ border-radius: 0;
+ padding: 0.2rem 0.7rem;
+ line-height: 1.4;
+}
+.case-card .case-header .industry-tag {
+ font-size: 0.82rem;
+ background: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ padding: 0.25rem 0.7rem;
+ color: #6E6E6E;
+ font-weight: 400;
+}
+
+.var-row {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-bottom: 0.85rem;
+}
+.var-row .var-name {
+ font-size: 0.82rem;
+ font-weight: 400;
+ color: #333333;
+ min-width: 110px;
+ flex-shrink: 0;
+}
+.var-row .var-bar-track {
+ flex: 1;
+ height: 8px;
+ background: #E5E5E5;
+ border-radius: 0;
+ overflow: hidden;
+}
+.var-row .var-bar-track .var-bar-fill {
+ height: 100%;
+ transition: width 0.4s ease;
+}
+.var-row .var-score {
+ font-size: 0.85rem;
+ font-weight: 700;
+ min-width: 32px;
+ text-align: right;
+ color: #111111;
+ font-variant-numeric: tabular-nums;
+}
+
+.var-bar-fill[data-level=low] {
+ background: #C7C7C7;
+}
+
+.var-bar-fill[data-level=mid] {
+ background: #8A8A8A;
+}
+
+.var-bar-fill[data-level=high] {
+ background: #111111;
+}
+
+.judgment-section {
+ margin-top: 1.5rem;
+ padding-top: 1.2rem;
+ border-top: 1px solid #CCCCCC;
+}
+.judgment-section .judgment-label {
+ font-size: 0.875rem;
+ font-weight: 700;
+ margin-bottom: 0.75rem;
+ color: #111111;
+}
+
+.confidence-section {
+ margin-top: 1.2rem;
+ padding: 1rem 1.2rem;
+ background: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+}
+.confidence-section .confidence-label {
+ font-size: 0.82rem;
+ font-weight: 700;
+ color: #111111;
+ margin-bottom: 0.6rem;
+ display: flex;
+ justify-content: space-between;
+}
+.confidence-section .confidence-slider {
+ width: 100%;
+ appearance: none;
+ height: 4px;
+ border-radius: 0;
+ background: #E5E5E5;
+ cursor: pointer;
+}
+.confidence-section .confidence-slider::-webkit-slider-thumb {
+ appearance: none;
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: #111111;
+ cursor: pointer;
+ border: none;
+}
+.confidence-section .confidence-slider::-moz-range-thumb {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: #111111;
+ border: none;
+ cursor: pointer;
+}
+.confidence-section .confidence-labels {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 0.4rem;
+}
+.confidence-section .confidence-labels .cl {
+ font-size: 0.7rem;
+ color: #6E6E6E;
+}
+
+.judgment-likert-row {
+ display: flex;
+ margin-top: 0.5rem;
+}
+
+.judgment-likert-btn {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.3rem;
+ padding: 0.7rem 0.3rem;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ background: #FFFFFF;
+ cursor: pointer;
+}
+.judgment-likert-btn + .judgment-likert-btn {
+ margin-left: -1px;
+}
+.judgment-likert-btn .jl-num {
+ font-size: 1.1rem;
+ font-weight: 800;
+ color: #6E6E6E;
+}
+.judgment-likert-btn .jl-label {
+ font-size: 0.65rem;
+ color: #6E6E6E;
+ text-align: center;
+ line-height: 1.3;
+}
+.judgment-likert-btn:hover {
+ border-color: #111111;
+ position: relative;
+ z-index: 1;
+}
+.judgment-likert-btn.active, .judgment-likert-btn[data-val].active {
+ border-color: #111111;
+ background: #111111;
+ position: relative;
+ z-index: 2;
+}
+.judgment-likert-btn.active .jl-num, .judgment-likert-btn.active .jl-label, .judgment-likert-btn[data-val].active .jl-num, .judgment-likert-btn[data-val].active .jl-label {
+ color: #FFFFFF;
+}
+
+.ahp-pair-card .pair-title {
+ font-size: 0.85rem;
+ color: #6E6E6E;
+ margin-bottom: 1.2rem;
+ font-weight: 400;
+}
+.ahp-pair-card .pair-criteria {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ margin-bottom: 1.2rem;
+}
+.ahp-pair-card .pair-criteria .crit {
+ flex: 1;
+ text-align: center;
+ font-size: 1rem;
+ font-weight: 400;
+ color: #6E6E6E;
+ padding: 0.6rem;
+ border-radius: 0;
+}
+.ahp-pair-card .pair-criteria .crit.crit-left {
+ text-align: right;
+}
+.ahp-pair-card .pair-criteria .crit.crit-right {
+ text-align: left;
+}
+.ahp-pair-card .pair-criteria .crit.crit-active-left, .ahp-pair-card .pair-criteria .crit.crit-active-right {
+ color: #111111;
+ font-weight: 800;
+}
+.ahp-pair-card .pair-criteria .vs-badge {
+ font-size: 0.75rem;
+ font-weight: 700;
+ color: #6E6E6E;
+ background: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ padding: 0.2rem 0.5rem;
+ flex-shrink: 0;
+}
+
+.ahp-slider-wrap {
+ padding: 0.5rem 0;
+}
+
+.ahp-scale-labels {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 0.5rem;
+ padding: 0 11px;
+}
+.ahp-scale-labels span {
+ font-size: 0.7rem;
+ color: #6E6E6E;
+ font-weight: 400;
+ text-align: center;
+ min-width: 0;
+}
+.ahp-scale-labels .sc-mid {
+ font-weight: 800;
+ color: #111111;
+ font-size: 0.72rem;
+}
+
+.ahp-slider {
+ width: 100%;
+ appearance: none;
+ height: 4px;
+ border-radius: 0;
+ background: #E5E5E5;
+ cursor: pointer;
+}
+.ahp-slider::-webkit-slider-thumb {
+ appearance: none;
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: #111111;
+ border: none;
+ cursor: pointer;
+}
+.ahp-slider::-moz-range-thumb {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: #111111;
+ border: none;
+ cursor: pointer;
+}
+
+.ahp-description {
+ text-align: center;
+ margin-top: 1rem;
+ padding: 0.7rem 1rem;
+ background: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ font-size: 0.875rem;
+ font-weight: 700;
+ color: #111111;
+ min-height: 42px;
+}
+
+.weight-chart {
+ margin-top: 0.5rem;
+}
+.weight-chart .wc-row {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-bottom: 0.7rem;
+}
+.weight-chart .wc-row .wc-label {
+ font-size: 0.82rem;
+ font-weight: 400;
+ color: #333333;
+ min-width: 110px;
+ flex-shrink: 0;
+}
+.weight-chart .wc-row .wc-track {
+ flex: 1;
+ height: 14px;
+ background: #E5E5E5;
+ border-radius: 0;
+ overflow: hidden;
+}
+.weight-chart .wc-row .wc-track .wc-fill {
+ height: 100%;
+ background: #111111;
+ transition: width 0.5s ease;
+}
+.weight-chart .wc-row .wc-pct {
+ font-size: 0.82rem;
+ font-weight: 700;
+ color: #111111;
+ min-width: 38px;
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+}
+
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+ gap: 0.75rem;
+ margin-top: 1rem;
+}
+.stats-grid .stat-box {
+ background: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ padding: 1rem;
+ text-align: center;
+}
+.stats-grid .stat-box .stat-val {
+ font-size: 1.6rem;
+ font-weight: 800;
+ color: #111111;
+ font-variant-numeric: tabular-nums;
+}
+.stats-grid .stat-box .stat-label {
+ font-size: 0.75rem;
+ color: #6E6E6E;
+ margin-top: 0.25rem;
+ line-height: 1.3;
+}
+.stats-grid .stat-box.stat-warn {
+ border-color: #111111;
+}
+.stats-grid .stat-box.stat-ok {
+ border-color: #CCCCCC;
+}
+
+.comparison-table-wrap {
+ overflow-x: auto;
+ margin-top: 1rem;
+ border: 1px solid #111111;
+ border-radius: 0;
+}
+.comparison-table-wrap table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.82rem;
+}
+.comparison-table-wrap table th {
+ background: #FFFFFF;
+ padding: 0.6rem 0.8rem;
+ text-align: center;
+ font-weight: 700;
+ border-bottom: 1px solid #111111;
+ white-space: nowrap;
+ color: #111111;
+}
+.comparison-table-wrap table td {
+ padding: 0.55rem 0.8rem;
+ text-align: center;
+ border-bottom: 1px solid #CCCCCC;
+ color: #333333;
+}
+.comparison-table-wrap table td:first-child {
+ font-weight: 700;
+ color: #111111;
+}
+.comparison-table-wrap table tr:last-child td {
+ border-bottom: none;
+}
+.comparison-table-wrap table tr.mismatch td {
+ background: #F5F5F5;
+ color: #111111;
+}
+.comparison-table-wrap table tr.mismatch td:first-child {
+ color: #111111;
+}
+
+.badge {
+ display: inline-block;
+ padding: 0.2rem 0.55rem;
+ border-radius: 0;
+ font-size: 0.72rem;
+ font-weight: 700;
+ line-height: 1.4;
+ border: 1px solid transparent;
+}
+.badge.badge-select {
+ background: #111111;
+ color: #FFFFFF;
+ border-color: #111111;
+}
+.badge.badge-reject {
+ background: #FFFFFF;
+ color: #6E6E6E;
+ border-color: #CCCCCC;
+}
+.badge.badge-match {
+ background: #FFFFFF;
+ color: #111111;
+ border-color: #111111;
+}
+.badge.badge-mismatch {
+ background: #111111;
+ color: #FFFFFF;
+ border-color: #111111;
+}
+.badge.badge-neutral {
+ background: #FFFFFF;
+ color: #6E6E6E;
+ border-color: #CCCCCC;
+}
+
+.neutral-row td {
+ background: #F5F5F5;
+ color: #333333;
+}
+
+.likert-group {
+ margin-bottom: 1.5rem;
+}
+.likert-group .likert-q {
+ font-size: 0.9rem;
+ font-weight: 700;
+ color: #111111;
+ margin-bottom: 0.75rem;
+ line-height: 1.5;
+}
+.likert-group .likert-options {
+ display: flex;
+}
+.likert-group .likert-options label {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.3rem;
+ cursor: pointer;
+}
+.likert-group .likert-options label + label .likert-btn {
+ margin-left: -1px;
+}
+.likert-group .likert-options label input[type=radio] {
+ display: none;
+}
+.likert-group .likert-options label .likert-btn {
+ width: 100%;
+ padding: 0.55rem 0.2rem;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ text-align: center;
+ font-size: 0.9rem;
+ font-weight: 700;
+ color: #6E6E6E;
+ background: #FFFFFF;
+}
+.likert-group .likert-options label .likert-desc {
+ font-size: 0.65rem;
+ color: #6E6E6E;
+ text-align: center;
+ line-height: 1.2;
+}
+.likert-group .likert-options label input:checked + .likert-btn {
+ border-color: #111111;
+ background: #111111;
+ color: #FFFFFF;
+ position: relative;
+ z-index: 1;
+}
+.likert-group .likert-options label:hover .likert-btn {
+ border-color: #111111;
+}
+
+.open-textarea {
+ width: 100%;
+ padding: 0.8rem 1rem;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ font-size: 0.875rem;
+ font-family: inherit;
+ color: #111111;
+ background: #FFFFFF;
+ resize: vertical;
+ min-height: 100px;
+}
+
+.complete-screen {
+ text-align: center;
+ padding: 2rem 1rem;
+}
+.complete-screen .complete-icon {
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
+ filter: grayscale(1);
+}
+.complete-screen .complete-title {
+ font-size: 1.5rem;
+ font-weight: 800;
+ letter-spacing: -0.02em;
+ margin-bottom: 0.5rem;
+}
+.complete-screen .complete-desc {
+ color: #6E6E6E;
+ font-size: 0.9rem;
+ margin-bottom: 2rem;
+ line-height: 1.7;
+}
+
+.json-output {
+ background: #FFFFFF;
+ color: #333333;
+ border: 1px solid #CCCCCC;
+ border-radius: 0;
+ padding: 1.2rem;
+ font-family: "Menlo", "Monaco", "Consolas", monospace;
+ font-size: 0.75rem;
+ line-height: 1.6;
+ text-align: left;
+ overflow-x: auto;
+ max-height: 400px;
+ overflow-y: auto;
+ white-space: pre;
+ margin-top: 1rem;
+}
+
+.divider {
+ height: 1px;
+ background: #CCCCCC;
+ margin: 1.5rem 0;
+}
+
+.text-muted {
+ color: #6E6E6E;
+ font-size: 0.85rem;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.mt-1 {
+ margin-top: 0.5rem;
+}
+
+.mt-2 {
+ margin-top: 1rem;
+}
+
+.mt-3 {
+ margin-top: 1.5rem;
+}
+
+.mb-1 {
+ margin-bottom: 0.5rem;
+}
+
+.highlight-text {
+ color: #111111;
+ font-weight: 800;
+}
+
+@media (max-width: 600px) {
+ #app-header {
+ padding: 0 1rem;
+ }
+ .header-title {
+ font-size: 0.78rem;
+ }
+ .card {
+ padding: 1.2rem;
+ }
+ .step-instruction {
+ font-size: 0.95rem;
+ margin-bottom: 1.2rem;
+ }
+ .step-progress .step-dot .dot-label {
+ display: none;
+ }
+ .var-row .var-name {
+ min-width: 80px;
+ font-size: 0.76rem;
+ }
+ .ahp-pair-card .pair-criteria {
+ flex-direction: column;
+ text-align: center;
+ gap: 0.5rem;
+ }
+ .ahp-pair-card .pair-criteria .crit {
+ text-align: center !important;
+ }
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ .comparison-table-wrap table {
+ font-size: 0.75rem;
+ }
+ .comparison-table-wrap table td,
+ .comparison-table-wrap table th {
+ padding: 0.4rem 0.5rem;
+ }
+ .likert-group .likert-options label .likert-desc {
+ display: none;
+ }
+ .btn.btn-lg {
+ padding: 0.75rem 1.5rem;
+ font-size: 0.9rem;
+ }
+}
+@media (max-width: 380px) {
+ .step-progress .step-dot .dot-circle {
+ width: 22px;
+ height: 22px;
+ font-size: 0.65rem;
+ }
+}
diff --git a/public/index.html b/public/index.html
index 3526504..259c71d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -12,7 +12,6 @@
-
diff --git a/public/js/app.js b/public/js/app.js
index 066b327..81d1447 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -1,95 +1,147 @@
+// ──────────────────────────────────────────
+// Text (한국어 전용)
+// ──────────────────────────────────────────
+const APP_TITLE = '스타트업 지원 심사 판단 실험';
+
+const GRADE_OPTIONS = ['1학년', '2학년', '3학년'];
+
+const VAR_LABELS = {
+ 안정성: '안정성',
+ 수익_가능성: '수익 가능성',
+ 자금_효율성: '자금 효율성',
+ 사업_계획_완성도: '사업 계획 완성도',
+ 사회적_가치: '사회적 가치',
+};
+
+const JUDGMENT_SCALE = ['무조건\n탈락', '탈락', '보통', '선발', '무조건\n선발'];
+
+const AHP_LEVELS = { 3: '약간', 5: '꽤', 7: '매우', 9: '극히' };
+
+const LIKERT_QUESTIONS = [
+ '모델의 판단 결과를 보고 놀라웠다.',
+ '내 직관보다 모델의 판단이 더 합리적이라고 생각한다.',
+ '이 선발 결정을 모델에게 맡겨도 괜찮다고 생각한다.',
+ '이번 판단은 온전히 내 의지로 한 것이라고 느낀다.',
+];
+
+const LIKERT_SCALE = [
+ '전혀\n아니다',
+ '아니다',
+ '보통',
+ '그렇다',
+ '매우\n그렇다',
+];
+
+const ALERT_FILL_ALL = '모든 항목을 입력해 주세요.';
+const ALERT_SELECT_JUDGMENT = '먼저 판단을 선택해 주세요.';
+const LEAVE_WARNING = '실험이 완료되지 않았습니다. 나가시겠습니까?';
+
// ──────────────────────────────────────────
// State
// ──────────────────────────────────────────
const state = {
- step: 1,
- started: false,
+ step: 1,
+ started: false,
- // Step 1
- meta: { grade: '', timestamp: null },
+ // Step 1
+ meta: { grade: '', timestamp: null },
- // Step 2
- shuffledCases: [],
- caseIndex: 0,
- currentJudgment: null, // 1-5 Likert
- caseStartTime: null,
- intuitive_judgments: [],
+ // Step 2
+ shuffledCases: [],
+ caseIndex: 0,
+ currentJudgment: null, // 1-5 Likert
+ caseStartTime: null,
+ intuitive_judgments: [],
- // Step 3
- pairIndex: 0,
- sliderValues: Array(10).fill(0),
- pairwiseRaw: null,
- ahpResult: null,
- crWarningShown: false,
- crWarningPending: false,
+ // Step 3
+ pairIndex: 0,
+ sliderValues: Array(10).fill(0),
+ pairwiseRaw: null,
+ ahpResult: null,
- // Step 4 / 5
- comparison: [],
- post_survey: { q1: null, q2: null, q3: null, q4: null, open_response: '' },
- submitted: false,
- _payload: null,
+ // Step 4 / 5
+ comparison: [],
+ post_survey: { q1: null, q2: null, q3: null, q4: null, open_response: '' },
+ submitted: false,
+ _payload: null,
};
// ──────────────────────────────────────────
// Render entry point
// ──────────────────────────────────────────
function renderApp() {
- renderHeader();
- renderMain();
+ renderHeader();
+ renderMain();
}
function renderHeader() {
- const el = document.getElementById('app-header');
- if (!el) return;
- el.innerHTML = `
+ const el = document.getElementById('app-header');
+ if (!el) return;
+ el.innerHTML = `
`;
}
function renderMain() {
- const el = document.getElementById('app-main');
- if (!el) return;
- switch (state.step) {
- case 1: el.innerHTML = renderStep1(); bindStep1(); break;
- case 2: el.innerHTML = renderStep2(); bindStep2(); break;
- case 3: el.innerHTML = renderStep3(); bindStep3(); break;
- case 4: el.innerHTML = renderStep4(); break;
- case 5: el.innerHTML = renderStep5(); bindStep5(); break;
- case 'complete': el.innerHTML = renderComplete(); bindComplete(); break;
- }
+ const el = document.getElementById('app-main');
+ if (!el) return;
+ switch (state.step) {
+ case 1:
+ el.innerHTML = renderStep1();
+ bindStep1();
+ break;
+ case 2:
+ el.innerHTML = renderStep2();
+ bindStep2();
+ break;
+ case 3:
+ el.innerHTML = renderStep3();
+ bindStep3();
+ break;
+ case 4:
+ el.innerHTML = renderStep4();
+ break;
+ case 5:
+ el.innerHTML = renderStep5();
+ bindStep5();
+ break;
+ case 'complete':
+ el.innerHTML = renderComplete();
+ bindComplete();
+ break;
+ }
}
// ──────────────────────────────────────────
// STEP 1
// ──────────────────────────────────────────
function renderStep1() {
- const { meta } = state;
- const notices = t('step1Notices').map(n => `${n}
`).join('');
- const gradeOpts = t('step1Q1opts').map(o => `
+ const { meta } = state;
+ const gradeOpts = GRADE_OPTIONS.map(
+ (o) => `
- `).join('');
+ `,
+ ).join('');
- return `
+ return `
-
${t('step1Title')}
-
${t('step1Intro')}
-
-
${notices}
+
스타트업 지원 심사 판단 실험
+
당신은 스타트업 지원 프로그램의 심사위원입니다. 제기되는 질문에 따라서 성실히 답변해주시면 감사하겠습니다.
총 소요 시간은 10분 이내입니다.
-
+
@@ -97,64 +149,73 @@ function renderStep1() {
}
function bindStep1() {
- document.getElementById('rg-grade').addEventListener('click', e => {
- const lbl = e.target.closest('label');
- if (!lbl) return;
- const val = lbl.querySelector('input').value;
- state.meta.grade = val;
- document.querySelectorAll('#rg-grade label').forEach(l =>
- l.classList.toggle('selected', l.querySelector('input').value === val));
- });
+ document.getElementById('rg-grade').addEventListener('click', (e) => {
+ const lbl = e.target.closest('label');
+ if (!lbl) return;
+ const val = lbl.querySelector('input').value;
+ state.meta.grade = val;
+ document
+ .querySelectorAll('#rg-grade label')
+ .forEach((l) =>
+ l.classList.toggle(
+ 'selected',
+ l.querySelector('input').value === val,
+ ),
+ );
+ });
- document.getElementById('btn-start').addEventListener('click', () => {
- if (!state.meta.grade) { alert(t('alertFillAll')); return; }
- state.meta.timestamp = Date.now();
- state.shuffledCases = shuffleCases();
- state.caseIndex = 0;
- state.started = true;
- enableLeaveWarning();
- goStep(2);
- });
+ document.getElementById('btn-start').addEventListener('click', () => {
+ if (!state.meta.grade) {
+ alert(ALERT_FILL_ALL);
+ return;
+ }
+ state.meta.timestamp = Date.now();
+ state.shuffledCases = shuffleCases();
+ state.caseIndex = 0;
+ state.started = true;
+ enableLeaveWarning();
+ goStep(2);
+ });
}
// ──────────────────────────────────────────
// STEP 2
// ──────────────────────────────────────────
function renderStep2() {
- const total = state.shuffledCases.length;
- const idx = state.caseIndex;
- const pct = Math.round((idx / total) * 100);
- const c = state.shuffledCases[idx];
- const varNames = t('varNames');
+ const total = state.shuffledCases.length;
+ const idx = state.caseIndex;
+ const pct = Math.round((idx / total) * 100);
+ const c = state.shuffledCases[idx];
- const varRows = VARS.map(key => {
- const score = c[key];
- const level = getScoreLevel(score);
- return `
+ const varRows = VARS.map((key) => {
+ const score = c[key];
+ const level = getScoreLevel(score);
+ return `
-
${varNames[key]}
+
${VAR_LABELS[key]}
${score}
`;
- }).join('');
+ }).join('');
- const scale = t('judgmentScale');
- const likertBtns = scale.map((label, i) => {
- const val = i + 1;
- const active = state.currentJudgment === val;
- return `
+ const likertBtns = JUDGMENT_SCALE.map((label, i) => {
+ const val = i + 1;
+ const active = state.currentJudgment === val;
+ return `
`;
- }).join('');
+ }).join('');
- return `
+ return `
+
당신은 스타트업 지원 프로그램의 심사위원입니다. 아래 팀의 정보를 보고 지원금 지급 가능성을 직관적으로 평가해주세요.
+
${idx + 1} / ${total}
@@ -172,107 +233,100 @@ function renderStep2() {
${varRows}
-
${t('judgmentLabel')}
+
이 팀의 지원금 지급 가능성을 평가하세요
${likertBtns}
-
+
-
-
${t('step2Instruction')}
`;
}
function bindStep2() {
- const caseStart = Date.now();
+ const caseStart = Date.now();
- document.getElementById('jl-row').addEventListener('click', e => {
- const btn = e.target.closest('.judgment-likert-btn');
- if (!btn) return;
- const val = parseInt(btn.dataset.val);
- state.currentJudgment = val;
- document.querySelectorAll('.judgment-likert-btn').forEach(b =>
- b.classList.toggle('active', parseInt(b.dataset.val) === val));
- document.getElementById('btn-case-next').disabled = false;
- });
+ document.getElementById('jl-row').addEventListener('click', (e) => {
+ const btn = e.target.closest('.judgment-likert-btn');
+ if (!btn) return;
+ const val = parseInt(btn.dataset.val);
+ state.currentJudgment = val;
+ document
+ .querySelectorAll('.judgment-likert-btn')
+ .forEach((b) =>
+ b.classList.toggle('active', parseInt(b.dataset.val) === val),
+ );
+ document.getElementById('btn-case-next').disabled = false;
+ });
- document.getElementById('btn-case-next').addEventListener('click', () => {
- if (state.currentJudgment === null) { alert(t('alertSelectJudgment')); return; }
- const elapsed = Date.now() - caseStart;
- const c = state.shuffledCases[state.caseIndex];
- state.intuitive_judgments.push({
- case_id: c.id,
- presented_order: state.caseIndex + 1,
- judgment: state.currentJudgment, // 1-5
- response_time_ms: elapsed,
+ document.getElementById('btn-case-next').addEventListener('click', () => {
+ if (state.currentJudgment === null) {
+ alert(ALERT_SELECT_JUDGMENT);
+ return;
+ }
+ const elapsed = Date.now() - caseStart;
+ const c = state.shuffledCases[state.caseIndex];
+ state.intuitive_judgments.push({
+ case_id: c.id,
+ presented_order: state.caseIndex + 1,
+ judgment: state.currentJudgment, // 1-5
+ response_time_ms: elapsed,
+ });
+ state.caseIndex++;
+ state.currentJudgment = null;
+ if (state.caseIndex >= state.shuffledCases.length) {
+ goStep(3);
+ } else {
+ renderMain();
+ }
});
- state.caseIndex++;
- state.currentJudgment = null;
- if (state.caseIndex >= state.shuffledCases.length) {
- goStep(3);
- } else {
- renderMain();
- }
- });
}
// ──────────────────────────────────────────
// STEP 3
// ──────────────────────────────────────────
-const AHP_PAIR_LABELS_KO = [
- ['안정성', '수익 가능성'],
- ['안정성', '자금 효율성'],
- ['안정성', '사업 계획 완성도'],
- ['안정성', '사회적 가치'],
- ['수익 가능성', '자금 효율성'],
- ['수익 가능성', '사업 계획 완성도'],
- ['수익 가능성', '사회적 가치'],
- ['자금 효율성', '사업 계획 완성도'],
- ['자금 효율성', '사회적 가치'],
- ['사업 계획 완성도', '사회적 가치'],
-];
-
-const AHP_PAIR_LABELS_EN = [
- ['Stability', 'Revenue Potential'],
- ['Stability', 'Capital Efficiency'],
- ['Stability', 'Business Plan Quality'],
- ['Stability', 'Social Value'],
- ['Revenue Potential', 'Capital Efficiency'],
- ['Revenue Potential', 'Business Plan Quality'],
- ['Revenue Potential', 'Social Value'],
- ['Capital Efficiency', 'Business Plan Quality'],
- ['Capital Efficiency', 'Social Value'],
- ['Business Plan Quality', 'Social Value'],
+const AHP_PAIR_LABELS = [
+ ['안정성', '수익 가능성'],
+ ['안정성', '자금 효율성'],
+ ['안정성', '사업 계획 완성도'],
+ ['안정성', '사회적 가치'],
+ ['수익 가능성', '자금 효율성'],
+ ['수익 가능성', '사업 계획 완성도'],
+ ['수익 가능성', '사회적 가치'],
+ ['자금 효율성', '사업 계획 완성도'],
+ ['자금 효율성', '사회적 가치'],
+ ['사업 계획 완성도', '사회적 가치'],
];
function getPairLabels(idx) {
- return currentLang === 'en' ? AHP_PAIR_LABELS_EN[idx] : AHP_PAIR_LABELS_KO[idx];
+ return AHP_PAIR_LABELS[idx];
}
function getAHPDescription(sliderPos, leftLabel, rightLabel) {
- if (sliderPos === 0) return t('ahpEqual');
- const ahpVal = Math.abs(sliderToStored(sliderPos)); // 3,5,7,9
- const level = t('ahpLevels')[ahpVal] || String(ahpVal);
- if (sliderPos < 0) return t('ahpLeftMore', leftLabel, level);
- return t('ahpRightMore', rightLabel, level);
+ if (sliderPos === 0) return '두 기준이 동등하게 중요합니다';
+ const ahpVal = Math.abs(sliderToStored(sliderPos)); // 3,5,7,9
+ const level = AHP_LEVELS[ahpVal] || String(ahpVal);
+ const crit = sliderPos < 0 ? leftLabel : rightLabel;
+ return `${crit}이(가) ${level} 더 중요합니다`;
}
function renderStep3() {
- const idx = state.pairIndex;
- const total = AHP_PAIRS.length;
- const pct = Math.round((idx / total) * 100);
- const [left, right] = getPairLabels(idx);
- const sliderVal = state.sliderValues[idx];
- const desc = getAHPDescription(sliderVal, left, right);
+ const idx = state.pairIndex;
+ const total = AHP_PAIRS.length;
+ const pct = Math.round((idx / total) * 100);
+ const [left, right] = getPairLabels(idx);
+ const sliderVal = state.sliderValues[idx];
+ const desc = getAHPDescription(sliderVal, left, right);
- const leftActive = sliderVal < 0 ? 'crit-active-left' : '';
- const rightActive = sliderVal > 0 ? 'crit-active-right' : '';
+ const leftActive = sliderVal < 0 ? 'crit-active-left' : '';
+ const rightActive = sliderVal > 0 ? 'crit-active-right' : '';
- return `
+ return `
+
제시되는 2개의 평가 기준 중 어느 것이 더 중요한지 비교해주세요. 왼쪽이 더 중요하면 슬라이더를 왼쪽으로, 오른쪽이 더 중요하면 오른쪽으로 움직이세요.
+
${idx + 1} / ${total}
@@ -283,8 +337,7 @@ function renderStep3() {
-
${t('step3Title')}
-
${t('step3Instruction')}
+
AHP 쌍대비교
@@ -306,153 +359,135 @@ function renderStep3() {
${desc}
- ${state.crWarningPending ? `
-
-
${t('crWarning')}
-
-
-
-
-
- ` : `
-
-
-
- `}
+
+
+
`;
}
function bindStep3() {
- const idx = state.pairIndex;
- const [left, right] = getPairLabels(idx);
+ const idx = state.pairIndex;
+ const [left, right] = getPairLabels(idx);
- const slider = document.getElementById('ahp-slider');
- if (slider) {
- slider.addEventListener('input', e => {
- const val = parseInt(e.target.value);
- state.sliderValues[idx] = val;
- document.getElementById('ahp-desc').textContent = getAHPDescription(val, left, right);
- document.getElementById('crit-left').className = `crit crit-left ${val < 0 ? 'crit-active-left' : ''}`;
- document.getElementById('crit-right').className = `crit crit-right ${val > 0 ? 'crit-active-right' : ''}`;
- });
- }
+ const slider = document.getElementById('ahp-slider');
+ if (slider) {
+ slider.addEventListener('input', (e) => {
+ const val = parseInt(e.target.value);
+ state.sliderValues[idx] = val;
+ document.getElementById('ahp-desc').textContent = getAHPDescription(
+ val,
+ left,
+ right,
+ );
+ document.getElementById('crit-left').className =
+ `crit crit-left ${val < 0 ? 'crit-active-left' : ''}`;
+ document.getElementById('crit-right').className =
+ `crit crit-right ${val > 0 ? 'crit-active-right' : ''}`;
+ });
+ }
- const nextBtn = document.getElementById('btn-pair-next');
- if (nextBtn) nextBtn.addEventListener('click', advancePair);
-
- const redoBtn = document.getElementById('btn-redo');
- if (redoBtn) redoBtn.addEventListener('click', () => {
- state.crWarningPending = false;
- state.pairIndex = 0;
- state.sliderValues = Array(10).fill(0);
- renderMain();
- });
-
- const contBtn = document.getElementById('btn-cr-continue');
- if (contBtn) contBtn.addEventListener('click', () => {
- state.crWarningPending = false;
- finalizeAHP();
- goStep(4);
- });
+ const nextBtn = document.getElementById('btn-pair-next');
+ if (nextBtn) nextBtn.addEventListener('click', advancePair);
}
function advancePair() {
- const total = AHP_PAIRS.length;
- if (state.pairIndex < total - 1) {
- state.pairIndex++;
- renderMain();
- } else {
- const raw = state.sliderValues.map(s => sliderToStored(s));
- state.pairwiseRaw = raw;
- const result = runAHP(raw);
- state.ahpResult = result;
-
- if (result.CR > 0.10) {
- state.crWarningPending = true;
- state.crWarningShown = true;
- renderMain();
+ const total = AHP_PAIRS.length;
+ if (state.pairIndex < total - 1) {
+ state.pairIndex++;
+ renderMain();
} else {
- finalizeAHP();
- goStep(4);
+ const raw = state.sliderValues.map((s) => sliderToStored(s));
+ state.pairwiseRaw = raw;
+ // CR은 분석용으로 계속 기록하되, 모순이 있어도 재입력 없이 그대로 진행
+ state.ahpResult = runAHP(raw);
+ finalizeAHP();
+ goStep(4);
}
- }
}
// Convert Likert 1-5 to judgment category for comparison with model
function likertToJudgment(likert) {
- if (likert >= 4) return 'select';
- if (likert <= 2) return 'reject';
- return 'neutral'; // 3
+ if (likert >= 4) return 'select';
+ if (likert <= 2) return 'reject';
+ return 'neutral'; // 3
}
function finalizeAHP() {
- const weights = state.ahpResult.weights;
- state.comparison = state.shuffledCases.map((c, i) => {
- const modelScore = calcModelScore(c, weights);
- const modelJudgment = modelScore >= 50 ? 'select' : 'reject';
- const rawLikert = state.intuitive_judgments[i]?.judgment ?? null;
- const intuitiveCategory = rawLikert !== null ? likertToJudgment(rawLikert) : null;
- const match = intuitiveCategory === 'neutral' ? null
- : intuitiveCategory === modelJudgment ? true : false;
- return {
- case_id: c.id,
- team: c.teamLetter,
- type: c.type,
- model_score: Math.round(modelScore * 10) / 10,
- model_judgment: modelJudgment,
- intuitive_likert: rawLikert,
- intuitive_category: intuitiveCategory,
- match,
- };
- });
+ const weights = state.ahpResult.weights;
+ state.comparison = state.shuffledCases.map((c, i) => {
+ const modelScore = calcModelScore(c, weights);
+ const modelJudgment = modelScore >= 50 ? 'select' : 'reject';
+ const rawLikert = state.intuitive_judgments[i]?.judgment ?? null;
+ const intuitiveCategory =
+ rawLikert !== null ? likertToJudgment(rawLikert) : null;
+ const match =
+ intuitiveCategory === 'neutral'
+ ? null
+ : intuitiveCategory === modelJudgment
+ ? true
+ : false;
+ return {
+ case_id: c.id,
+ team: c.teamLetter,
+ type: c.type,
+ model_score: Math.round(modelScore * 10) / 10,
+ model_judgment: modelJudgment,
+ intuitive_likert: rawLikert,
+ intuitive_category: intuitiveCategory,
+ match,
+ };
+ });
}
// ──────────────────────────────────────────
// STEP 4
// ──────────────────────────────────────────
function renderStep4() {
- const weights = state.ahpResult.weights;
- const varNames = t('varNames');
+ const weights = state.ahpResult.weights;
- const weightRows = VARS.map((key, i) => {
- const pct = Math.round(weights[i] * 100);
- return `
+ const weightRows = VARS.map((key, i) => {
+ const pct = Math.round(weights[i] * 100);
+ return `
-
${varNames[key]}
+
${VAR_LABELS[key]}
${pct}%
`;
- }).join('');
+ }).join('');
- const comp = state.comparison;
- // Exclude neutral (match===null) from mismatch counts
- const decidable = comp.filter(c => c.match !== null);
- const totalMismatch = decidable.filter(c => !c.match).length;
- const baseline = decidable.filter(c => c.type === 'baseline');
- const spike = decidable.filter(c => c.type === 'spike');
- const conflict = decidable.filter(c => c.type === 'conflict');
- const { CR } = state.ahpResult;
+ const comp = state.comparison;
+ // Exclude neutral (match===null) from mismatch counts
+ const decidable = comp.filter((c) => c.match !== null);
+ const totalMismatch = decidable.filter((c) => !c.match).length;
+ const baseline = decidable.filter((c) => c.type === 'baseline');
+ const spike = decidable.filter((c) => c.type === 'spike');
+ const conflict = decidable.filter((c) => c.type === 'conflict');
+ const { CR } = state.ahpResult;
- const rows = comp.map(c => {
- const mis = c.match === false;
- const neu = c.match === null;
- const mLabel = c.model_judgment === 'select'
- ? `
${t('select')}`
- : `
${t('reject')}`;
- const iLabel = c.intuitive_category === 'select'
- ? `
${c.intuitive_likert}`
- : c.intuitive_category === 'reject'
- ? `
${c.intuitive_likert}`
- : `
${c.intuitive_likert}`;
- const matchBadge = c.match === true
- ? `
${t('colYes')}`
- : c.match === false
- ? `
${t('colNo')}`
- : `
${t('colNeutral')}`;
- return `
+ const rows = comp
+ .map((c) => {
+ const mis = c.match === false;
+ const neu = c.match === null;
+ const mLabel =
+ c.model_judgment === 'select'
+ ? `
선발`
+ : `
탈락`;
+ const iLabel =
+ c.intuitive_category === 'select'
+ ? `
${c.intuitive_likert}`
+ : c.intuitive_category === 'reject'
+ ? `
${c.intuitive_likert}`
+ : `
${c.intuitive_likert}`;
+ const matchBadge =
+ c.match === true
+ ? `
✓`
+ : c.match === false
+ ? `
✗`
+ : `
△`;
+ return `
| Team ${c.team} |
${c.model_score} |
@@ -461,54 +496,55 @@ function renderStep4() {
${matchBadge} |
`;
- }).join('');
+ })
+ .join('');
- return `
+ return `
-
${t('step4Title')}
-
${t('weightsTitle')}
+
결과 비교
+
나의 판단 기준 (AHP 가중치)
${weightRows}
-
${t('statsTitle')}
+
핵심 통계
${totalMismatch}/${decidable.length}
-
${t('statTotal')}
+
전체 불일치율
-
${baseline.filter(c=>!c.match).length}/${baseline.length}
-
${t('statBaseline')}
+
${baseline.filter((c) => !c.match).length}/${baseline.length}
+
베이스라인
불일치율
-
${spike.filter(c=>!c.match).length}/${spike.length}
-
${t('statSpike')}
+
${spike.filter((c) => !c.match).length}/${spike.length}
+
스파이크 사례
불일치율
-
${conflict.filter(c=>!c.match).length}/${conflict.length}
-
${t('statConflict')}
+
${conflict.filter((c) => !c.match).length}/${conflict.length}
+
충돌 사례
불일치율
-
+
${CR.toFixed(3)}
-
${t('statCR')}
+
CR (일관성 비율)
-
${t('totalMismatch', totalMismatch, decidable.length)}
+
총 불일치: ${totalMismatch} / ${decidable.length}건 (보통 제외)
-
${t('tableTitle')}
+
사례별 모델 vs 직관 비교
- | ${t('colTeam')} |
- ${t('colModelScore')} |
- ${t('colModelJudge')} |
- ${t('colIntuition')} |
- ${t('colMatch')} |
+ 팀 |
+ 모델 점수 |
+ 모델 판단 |
+ 직관 (1-5) |
+ 일치 |
${rows}
@@ -517,7 +553,7 @@ function renderStep4() {
-
+
`;
@@ -527,41 +563,38 @@ function renderStep4() {
// STEP 5
// ──────────────────────────────────────────
function renderStep5() {
- const qs = t('likertQ');
- const scale = t('likertScale');
-
- const groups = qs.map((q, qi) => {
- const opts = scale.map((label, si) => {
- const val = si + 1;
- const checked = state.post_survey[`q${qi+1}`] === val;
- return `
+ const groups = LIKERT_QUESTIONS.map((q, qi) => {
+ const opts = LIKERT_SCALE.map((label, si) => {
+ const val = si + 1;
+ const checked = state.post_survey[`q${qi + 1}`] === val;
+ return `
`;
- }).join('');
- return `
+ }).join('');
+ return `
-
${qi+1}. ${q}
+
${qi + 1}. ${q}
${opts}
`;
- }).join('');
+ }).join('');
- return `
+ return `
-
${t('step5Title')}
-
${t('step5Desc')}
+
사후 설문
+
마지막으로 간단한 설문에 답해주세요.
${groups}
-
-
+
+
-
+
@@ -569,68 +602,73 @@ function renderStep5() {
}
function bindStep5() {
- document.querySelectorAll('.likert-options input[type="radio"]').forEach(inp => {
- inp.addEventListener('change', e => {
- state.post_survey[e.target.name] = parseInt(e.target.value);
+ document
+ .querySelectorAll('.likert-options input[type="radio"]')
+ .forEach((inp) => {
+ inp.addEventListener('change', (e) => {
+ state.post_survey[e.target.name] = parseInt(e.target.value);
+ });
+ });
+ document.getElementById('open-resp').addEventListener('input', (e) => {
+ state.post_survey.open_response = e.target.value;
+ });
+ document.getElementById('btn-submit').addEventListener('click', () => {
+ const { q1, q2, q3, q4 } = state.post_survey;
+ if (!q1 || !q2 || !q3 || !q4) {
+ alert(ALERT_FILL_ALL);
+ return;
+ }
+ submitData();
});
- });
- document.getElementById('open-resp').addEventListener('input', e => {
- state.post_survey.open_response = e.target.value;
- });
- document.getElementById('btn-submit').addEventListener('click', () => {
- const { q1, q2, q3, q4 } = state.post_survey;
- if (!q1 || !q2 || !q3 || !q4) { alert(t('alertFillAll')); return; }
- submitData();
- });
}
// ──────────────────────────────────────────
// Submit & Complete
// ──────────────────────────────────────────
function buildPayload() {
- return {
- meta: { timestamp: state.meta.timestamp, grade: state.meta.grade },
- intuitive_judgments: state.intuitive_judgments,
- ahp: {
- pairwise_raw: state.pairwiseRaw,
- weights: state.ahpResult.weights,
- lambda_max: state.ahpResult.lambdaMax,
- CI: state.ahpResult.CI,
- CR: state.ahpResult.CR,
- cr_warning_shown: state.crWarningShown,
- },
- comparison: state.comparison,
- post_survey: state.post_survey,
- };
+ return {
+ meta: { timestamp: state.meta.timestamp, grade: state.meta.grade },
+ intuitive_judgments: state.intuitive_judgments,
+ ahp: {
+ pairwise_raw: state.pairwiseRaw,
+ weights: state.ahpResult.weights,
+ lambda_max: state.ahpResult.lambdaMax,
+ CI: state.ahpResult.CI,
+ CR: state.ahpResult.CR,
+ cr_warning_shown: false, // CR 경고 UI 제거 — CSV 컬럼 호환용으로 유지
+ },
+ comparison: state.comparison,
+ post_survey: state.post_survey,
+ };
}
async function submitData() {
- if (state.submitted) return;
- state.submitted = true;
- disableLeaveWarning();
- const payload = buildPayload();
- try {
- await fetch('/api/submit', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- });
- } catch (e) {
- console.warn('Server submit failed:', e);
- }
- state._payload = payload;
- state.step = 'complete';
- renderApp();
+ if (state.submitted) return;
+ state.submitted = true;
+ disableLeaveWarning();
+ const payload = buildPayload();
+ try {
+ await fetch('/api/submit', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+ } catch (e) {
+ console.warn('Server submit failed:', e);
+ }
+ state._payload = payload;
+ state.step = 'complete';
+ renderApp();
}
function renderComplete() {
- return `
+ return `
-
🎉
-
${t('completeTitle')}
-
${t('completeDesc')}
+
참여해 주셔서 감사합니다!
+
실험이 완료되었습니다.
+소중한 시간을 내어 참여해 주셔서 진심으로 감사드립니다.
@@ -643,27 +681,32 @@ function bindComplete() {}
// Navigation & utilities
// ──────────────────────────────────────────
function goStep(n) {
- state.step = n;
- renderApp();
- window.scrollTo({ top: 0, behavior: 'smooth' });
+ state.step = n;
+ renderApp();
+ window.scrollTo({ top: 0, behavior: 'smooth' });
}
function enableLeaveWarning() {
- window._leaveHandler = e => { e.preventDefault(); e.returnValue = t('leaveWarning'); };
- window.addEventListener('beforeunload', window._leaveHandler);
+ window._leaveHandler = (e) => {
+ e.preventDefault();
+ e.returnValue = LEAVE_WARNING;
+ };
+ window.addEventListener('beforeunload', window._leaveHandler);
}
function disableLeaveWarning() {
- if (window._leaveHandler) {
- window.removeEventListener('beforeunload', window._leaveHandler);
- window._leaveHandler = null;
- }
+ if (window._leaveHandler) {
+ window.removeEventListener('beforeunload', window._leaveHandler);
+ window._leaveHandler = null;
+ }
}
function escHtml(str) {
- return String(str)
- .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
+ return String(str)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
}
document.addEventListener('DOMContentLoaded', renderApp);
diff --git a/public/js/i18n.js b/public/js/i18n.js
deleted file mode 100644
index 8e01621..0000000
--- a/public/js/i18n.js
+++ /dev/null
@@ -1,187 +0,0 @@
-const I18N = {
- ko: {
- appTitle: '스타트업 지원 심사 판단 실험',
- langBtn: 'EN',
-
- stepLabels: ['안내', '직관 판단', 'AHP 비교', '결과', '사후 설문'],
-
- // Step 1
- step1Title: '스타트업 지원 심사 판단 실험',
- step1Intro: '당신은 스타트업 지원 프로그램의 심사위원입니다. 이 실험은 두 가지 판단 방식 — 인간의 직관과 AI 기반 의사결정 모델(AHP) — 을 비교 분석합니다. 15개의 스타트업 사례를 보고 지원금 지급 가능성을 평가한 뒤, 본인만의 평가 기준 가중치를 설정하게 됩니다.',
- step1Notices: [
- '정답은 없습니다. 느낌과 직관대로 판단해 주세요.',
- '총 소요 시간은 약 20~25분입니다.',
- '중간에 페이지를 나가면 데이터가 저장되지 않습니다.',
- ],
- step1Q1: '학년',
- step1Q1opts: ['1학년', '2학년', '3학년'],
- startBtn: '실험 시작',
-
- // Step 2
- step2Instruction: '당신은 스타트업 지원 프로그램의 심사위원입니다. 아래 팀의 정보를 보고 지원금 지급 가능성을 직관적으로 평가하세요. 계산하지 말고 느낌대로 판단하세요.',
- varNames: {
- '안정성': '안정성',
- '수익_가능성': '수익 가능성',
- '자금_효율성': '자금 효율성',
- '사업_계획_완성도': '사업 계획 완성도',
- '사회적_가치': '사회적 가치',
- },
- judgmentLabel: '이 팀의 지원금 지급 가능성을 평가하세요',
- judgmentScale: ['매우\n탈락', '탈락', '보통', '선발', '매우\n선발'],
- btnNext: '다음',
-
- // Step 3
- step3Title: 'AHP 쌍대비교',
- step3Instruction: '5개의 평가 기준 중 어느 것이 더 중요한지 비교해주세요. 왼쪽이 더 중요하면 슬라이더를 왼쪽으로, 오른쪽이 더 중요하면 오른쪽으로 움직이세요.',
- ahpEqual: '두 기준이 동등하게 중요합니다',
- ahpLeftMore: (crit, level) => `${crit}이(가) ${level} 더 중요합니다`,
- ahpRightMore: (crit, level) => `${crit}이(가) ${level} 더 중요합니다`,
- ahpLevels: { 3: '약간', 5: '꽤', 7: '매우', 9: '극히' },
- crWarning: '판단에 약간의 모순이 감지되었습니다(CR > 0.10). 다시 입력하시겠습니까?',
- btnRedo: '다시 입력',
- btnContinue: '그대로 진행',
-
- // Step 4
- step4Title: '결과 비교',
- weightsTitle: '나의 판단 기준 (AHP 가중치)',
- tableTitle: '사례별 모델 vs 직관 비교',
- colTeam: '팀',
- colModelScore: '모델 점수',
- colModelJudge: '모델 판단',
- colIntuition: '직관 (1-5)',
- colMatch: '일치',
- colYes: '✓',
- colNo: '✗',
- colNeutral: '△',
- totalMismatch: (n, t) => `총 불일치: ${n} / ${t}건 (보통 제외)`,
- statsTitle: '핵심 통계',
- statTotal: '전체 불일치율',
- statBaseline: '베이스라인\n불일치율',
- statSpike: '스파이크 사례\n불일치율',
- statConflict: '충돌 사례\n불일치율',
- statCR: 'CR (일관성 비율)',
- select: '선발',
- reject: '탈락',
- neutral: '보통',
- btnGoSurvey: '사후 설문으로 이동',
-
- // Step 5
- step5Title: '사후 설문',
- step5Desc: '마지막으로 간단한 설문에 답해주세요.',
- likertQ: [
- '모델의 판단 결과를 보고 놀라웠다.',
- '내 직관보다 모델의 판단이 더 합리적이라고 생각한다.',
- '이 선발 결정을 모델에게 맡겨도 괜찮다고 생각한다.',
- '이번 판단은 온전히 내 의지로 한 것이라고 느낀다.',
- ],
- likertScale: ['전혀\n아니다', '아니다', '보통', '그렇다', '매우\n그렇다'],
- openQ: '모델 판단과 직관이 달랐을 때 어떤 생각이 들었나요? (선택)',
- openPh: '자유롭게 작성해 주세요...',
- btnSubmit: '제출하기',
-
- // Complete
- completeTitle: '참여해 주셔서 감사합니다!',
- completeDesc: '실험이 완료되었습니다.\n소중한 시간을 내어 참여해 주셔서 진심으로 감사드립니다.\n응답 데이터는 안전하게 저장되었습니다.',
-
- alertFillAll: '모든 항목을 입력해 주세요.',
- alertSelectJudgment: '먼저 판단을 선택해 주세요.',
- leaveWarning: '실험이 완료되지 않았습니다. 나가시겠습니까?',
- },
-
- en: {
- appTitle: 'Startup Funding Evaluation Experiment',
- langBtn: '한국어',
-
- stepLabels: ['Intro', 'Intuition', 'AHP', 'Results', 'Survey'],
-
- step1Title: 'Startup Funding Evaluation Experiment',
- step1Intro: 'You are a judge in a startup support program. This experiment compares two decision-making approaches — human intuition and an AI-based model (AHP). You will evaluate 15 startup cases and then set your own weighting criteria.',
- step1Notices: [
- 'There are no right or wrong answers. Judge by your gut feeling.',
- 'The experiment takes approximately 20–25 minutes.',
- 'Leaving the page mid-experiment will result in lost data.',
- ],
- step1Q1: 'Grade',
- step1Q1opts: ['Grade 1', 'Grade 2', 'Grade 3'],
- startBtn: 'Start Experiment',
-
- step2Instruction: 'You are a judge in a startup support program. Based on the team\'s information below, intuitively evaluate their funding potential. Do not calculate — go with your gut.',
- varNames: {
- '안정성': 'Stability',
- '수익_가능성': 'Revenue Potential',
- '자금_효율성': 'Capital Efficiency',
- '사업_계획_완성도': 'Business Plan Quality',
- '사회적_가치': 'Social Value',
- },
- judgmentLabel: 'Evaluate this team\'s funding potential',
- judgmentScale: ['Strongly\nReject', 'Reject', 'Neutral', 'Fund', 'Strongly\nFund'],
- btnNext: 'Next',
-
- step3Title: 'AHP Pairwise Comparison',
- step3Instruction: 'Compare which of the 5 criteria matters more. Move the slider left if the left criterion is more important, right if the right one is.',
- ahpEqual: 'Both criteria are equally important',
- ahpLeftMore: (crit, level) => `${crit} is ${level} more important`,
- ahpRightMore: (crit, level) => `${crit} is ${level} more important`,
- ahpLevels: { 3: 'somewhat', 5: 'considerably', 7: 'much', 9: 'extremely' },
- crWarning: 'Some inconsistency detected (CR > 0.10). Would you like to redo the comparison?',
- btnRedo: 'Redo',
- btnContinue: 'Continue anyway',
-
- step4Title: 'Results Comparison',
- weightsTitle: 'Your Judgment Criteria (AHP Weights)',
- tableTitle: 'Model vs. Intuition per Case',
- colTeam: 'Team',
- colModelScore: 'Model Score',
- colModelJudge: 'Model',
- colIntuition: 'Intuition (1-5)',
- colMatch: 'Match',
- colYes: '✓',
- colNo: '✗',
- colNeutral: '△',
- totalMismatch: (n, tot) => `Total mismatches: ${n} / ${tot} (neutral excluded)`,
- statsTitle: 'Key Statistics',
- statTotal: 'Overall Mismatch Rate',
- statBaseline: 'Baseline\nMismatch Rate',
- statSpike: 'Spike Case\nMismatch Rate',
- statConflict: 'Conflict Case\nMismatch Rate',
- statCR: 'CR (Consistency Ratio)',
- select: 'Fund',
- reject: 'Reject',
- neutral: 'Neutral',
- btnGoSurvey: 'Go to Post-Survey',
-
- step5Title: 'Post-Survey',
- step5Desc: 'Please answer a few final questions.',
- likertQ: [
- 'I was surprised by the model\'s judgment.',
- 'I think the model\'s judgment is more rational than my intuition.',
- 'I would be comfortable letting the model make these selection decisions.',
- 'I feel the decisions I made were entirely my own.',
- ],
- likertScale: ['Strongly\nDisagree', 'Disagree', 'Neutral', 'Agree', 'Strongly\nAgree'],
- openQ: 'What did you think when your intuition differed from the model? (Optional)',
- openPh: 'Write freely...',
- btnSubmit: 'Submit',
-
- completeTitle: 'Thank you for participating!',
- completeDesc: 'The experiment is complete.\nThank you sincerely for your time and participation.\nYour responses have been saved securely.',
-
- alertFillAll: 'Please fill in all fields.',
- alertSelectJudgment: 'Please make a judgment first.',
- leaveWarning: 'The experiment is not complete. Are you sure you want to leave?',
- },
-};
-
-let currentLang = 'ko';
-
-function t(key, ...args) {
- const val = I18N[currentLang][key];
- if (typeof val === 'function') return val(...args);
- return val ?? key;
-}
-
-function switchLang() {
- currentLang = currentLang === 'ko' ? 'en' : 'ko';
- document.documentElement.lang = currentLang;
- if (typeof renderApp === 'function') renderApp();
-}
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 8777a23..2d29614 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -1,21 +1,16 @@
// ──────────────────────────────────────────
-// Variables
+// Variables — monochrome only
// ──────────────────────────────────────────
-$primary: #2563EB;
-$primary-dk: #1D4ED8;
-$success: #16A34A;
-$danger: #DC2626;
-$warning: #D97706;
-$muted: #6B7280;
-$border: #E5E7EB;
-$bg: #F3F4F6;
-$card-bg: #FFFFFF;
-$text: #111827;
-$text-sm: #374151;
+$ink: #111111; // 본문 / 강조 (검정)
+$ink-soft: #333333;
+$muted: #6E6E6E; // 보조 텍스트 (회색)
+$line: #111111; // 구조 구분선 (검정 border)
+$line-soft: #CCCCCC; // 입력/바/내부 구분선 (회색)
+$track: #E5E5E5; // 바 트랙 (회색)
+$wash: #F5F5F5; // 유일하게 허용하는 옅은 회색 면
+$white: #FFFFFF;
-$radius: 12px;
-$radius-sm: 8px;
-$shadow: 0 1px 4px rgba(0,0,0,.10), 0 4px 16px rgba(0,0,0,.06);
+$radius: 0;
// ──────────────────────────────────────────
// Reset & Base
@@ -26,25 +21,29 @@ html { font-size: 16px; scroll-behavior: smooth; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans KR', sans-serif;
- background: $bg;
- color: $text;
+ background: $white;
+ color: $ink;
line-height: 1.6;
min-height: 100vh;
+ -webkit-font-smoothing: antialiased;
}
+// 클릭 요소 focus 시 별도 디자인 없음
+:focus,
+:focus-visible { outline: none; }
+
// ──────────────────────────────────────────
// Layout
// ──────────────────────────────────────────
#app { display: flex; flex-direction: column; min-height: 100vh; }
#app-header {
- background: $card-bg;
- border-bottom: 1px solid $border;
+ background: $white;
+ border-bottom: 1px solid $line;
padding: 0 1.5rem;
position: sticky;
top: 0;
z-index: 100;
- box-shadow: 0 1px 8px rgba(0,0,0,.06);
}
.header-inner {
@@ -58,33 +57,40 @@ body {
.header-title {
font-size: .85rem;
- font-weight: 600;
- color: $primary;
+ font-weight: 700;
+ color: $ink;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ letter-spacing: -.01em;
}
-.lang-btn {
- font-size: .78rem;
- padding: .3rem .8rem;
- border: 1px solid $border;
- border-radius: 20px;
- background: transparent;
- cursor: pointer;
- color: $muted;
- transition: all .15s;
+.header-name {
+ direction: rtl;
+ font-size: .85rem;
+ font-weight: 700;
+ color: $ink;
+ flex: 1;
white-space: nowrap;
- &:hover { border-color: $primary; color: $primary; }
+ overflow: hidden;
+ text-overflow: ellipsis;
+ letter-spacing: -.01em;
}
+
#app-main {
flex: 1;
padding: 2rem 1rem 4rem;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
.step-wrap {
+ // #app-main이 flex 컨테이너라 폭을 명시하지 않으면 콘텐츠 크기로 줄어듦
+ width: 100%;
max-width: 720px;
margin: 0 auto;
}
@@ -112,26 +118,24 @@ body {
top: 14px;
left: calc(50% + 14px);
right: calc(-50% + 14px);
- height: 2px;
- background: $border;
- transition: background .3s;
+ height: 1px;
+ background: $line-soft;
}
- &.done::after { background: $primary; }
+ &.done::after { background: $ink; }
.dot-circle {
width: 28px;
height: 28px;
border-radius: 50%;
- border: 2px solid $border;
- background: $card-bg;
+ border: 1px solid $line-soft;
+ background: $white;
display: flex;
align-items: center;
justify-content: center;
font-size: .72rem;
font-weight: 700;
color: $muted;
- transition: all .3s;
position: relative;
z-index: 1;
}
@@ -143,9 +147,9 @@ body {
line-height: 1.2;
}
- &.done .dot-circle { background: $primary; border-color: $primary; color: #fff; }
- &.active .dot-circle { background: $primary; border-color: $primary; color: #fff; box-shadow: 0 0 0 4px rgba($primary, .2); }
- &.active .dot-label { color: $primary; font-weight: 600; }
+ &.done .dot-circle { background: $ink; border-color: $ink; color: $white; }
+ &.active .dot-circle { background: $ink; border-color: $ink; color: $white; }
+ &.active .dot-label { color: $ink; font-weight: 700; }
}
}
@@ -153,9 +157,9 @@ body {
// Cards
// ──────────────────────────────────────────
.card {
- background: $card-bg;
+ background: $white;
+ border: 1px solid $line;
border-radius: $radius;
- box-shadow: $shadow;
padding: 2rem;
margin-bottom: 1.5rem;
@@ -164,7 +168,9 @@ body {
.section-title {
font-size: 1.3rem;
- font-weight: 700;
+ font-weight: 800;
+ color: $ink;
+ letter-spacing: -.02em;
margin-bottom: .4rem;
}
@@ -184,65 +190,56 @@ body {
label {
display: block;
font-size: .875rem;
- font-weight: 600;
+ font-weight: 700;
margin-bottom: .5rem;
- color: $text-sm;
+ color: $ink;
}
input[type="text"] {
width: 100%;
padding: .6rem .9rem;
- border: 1.5px solid $border;
- border-radius: $radius-sm;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
font-size: .9rem;
- outline: none;
- transition: border-color .2s;
- &:focus { border-color: $primary; }
- &::placeholder { color: #9CA3AF; }
+ color: $ink;
+ background: $white;
+ &::placeholder { color: #A3A3A3; }
}
}
.radio-group {
display: flex;
flex-wrap: wrap;
- gap: .6rem;
+ gap: .5rem;
label {
display: flex;
align-items: center;
gap: .4rem;
padding: .5rem 1rem;
- border: 1.5px solid $border;
- border-radius: 24px;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
cursor: pointer;
font-size: .875rem;
- font-weight: normal;
- color: $text-sm;
- transition: all .15s;
+ font-weight: 400;
+ color: $muted;
margin: 0;
white-space: nowrap;
input[type="radio"] { display: none; }
- &:hover { border-color: $primary; color: $primary; }
- &.selected { border-color: $primary; background: rgba($primary,.07); color: $primary; font-weight: 600; }
+ &:hover { border-color: $line; color: $ink; }
+ &.selected { border-color: $ink; background: $ink; color: $white; font-weight: 700; }
}
}
-.notice-box {
- background: #EFF6FF;
- border: 1px solid #BFDBFE;
- border-radius: $radius-sm;
- padding: 1rem 1.2rem;
+// 카드 바깥에 놓이는 단계 안내문 — 본문보다 크게
+.step-instruction {
+ font-size: 1.05rem;
+ line-height: 1.65;
+ font-weight: 500;
+ color: $ink;
margin-bottom: 1.5rem;
-
- p {
- font-size: .85rem;
- color: #1E40AF;
- line-height: 1.6;
- &::before { content: '• '; }
- + p { margin-top: .3rem; }
- }
}
// ──────────────────────────────────────────
@@ -254,60 +251,62 @@ body {
justify-content: center;
gap: .4rem;
padding: .65rem 1.6rem;
- border-radius: $radius-sm;
- border: none;
+ border-radius: $radius;
+ border: 1px solid transparent;
font-size: .9rem;
- font-weight: 600;
+ font-weight: 700;
cursor: pointer;
- transition: all .15s;
text-decoration: none;
+ background: $white;
+ color: $ink;
- &:disabled { opacity: .45; cursor: not-allowed; }
+ &:disabled { opacity: .35; cursor: not-allowed; }
&.btn-primary {
- background: $primary;
- color: #fff;
- &:not(:disabled):hover { background: $primary-dk; transform: translateY(-1px); box-shadow: 0 4px 12px rgba($primary,.35); }
+ background: $ink;
+ color: $white;
+ border-color: $ink;
+ &:not(:disabled):hover { background: $white; color: $ink; }
}
&.btn-outline {
- background: transparent;
- color: $primary;
- border: 1.5px solid $primary;
- &:hover { background: rgba($primary,.06); }
+ background: $white;
+ color: $ink;
+ border-color: $ink;
+ &:not(:disabled):hover { background: $ink; color: $white; }
}
&.btn-gray {
- background: #F3F4F6;
- color: $text-sm;
- border: 1.5px solid $border;
- &:not(:disabled):hover { background: $border; }
+ background: $white;
+ color: $muted;
+ border-color: $line-soft;
+ &:not(:disabled):hover { border-color: $line; color: $ink; }
}
- &.btn-lg { padding: .85rem 2.2rem; font-size: 1rem; border-radius: 10px; }
+ &.btn-lg { padding: .85rem 2.2rem; font-size: 1rem; }
&.btn-full { width: 100%; }
- &.btn-select {
- background: $primary;
- color: #fff;
- flex: 1;
- font-size: 1rem;
- padding: .9rem 1rem;
- &:not(:disabled):hover { background: $primary-dk; }
- }
-
+ // 선정 / 미선정 — 미선택 상태는 회색 테두리, 선택 시 검정 반전
+ &.btn-select,
&.btn-reject {
- background: #F9FAFB;
- color: #6B7280;
- border: 1.5px solid $border;
flex: 1;
font-size: 1rem;
padding: .9rem 1rem;
- &:not(:disabled):hover { background: #F3F4F6; color: #374151; }
+ background: $white;
+ color: $ink;
+ border-color: $line-soft;
+ &:not(:disabled):hover { border-color: $line; }
}
- &.btn-selected-select { background: $success; color: #fff; flex: 1; padding: .9rem 1rem; font-size: 1rem; }
- &.btn-selected-reject { background: $danger; color: #fff; flex: 1; padding: .9rem 1rem; font-size: 1rem; }
+ &.btn-selected-select,
+ &.btn-selected-reject {
+ flex: 1;
+ padding: .9rem 1rem;
+ font-size: 1rem;
+ background: $ink;
+ color: $white;
+ border-color: $ink;
+ }
}
.btn-row {
@@ -330,18 +329,17 @@ body {
font-size: .82rem;
color: $muted;
margin-bottom: .4rem;
- font-weight: 600;
+ font-weight: 700;
}
.progress-track {
- height: 6px;
- background: $border;
- border-radius: 3px;
+ height: 4px;
+ background: $track;
+ border-radius: $radius;
overflow: hidden;
.progress-fill {
height: 100%;
- background: $primary;
- border-radius: 3px;
- transition: width .4s ease;
+ background: $ink;
+ transition: width .3s ease;
}
}
}
@@ -353,25 +351,27 @@ body {
gap: .75rem;
margin-bottom: 1.4rem;
padding-bottom: 1rem;
- border-bottom: 1px solid $border;
+ border-bottom: 1px solid $line-soft;
.team-badge {
font-size: 1.4rem;
font-weight: 800;
- color: $primary;
- background: rgba($primary,.08);
- border-radius: $radius-sm;
- padding: .3rem .8rem;
+ color: $ink;
+ background: $white;
+ border: 1px solid $line;
+ border-radius: $radius;
+ padding: .2rem .7rem;
+ line-height: 1.4;
}
.industry-tag {
font-size: .82rem;
- background: #F3F4F6;
- border: 1px solid $border;
- border-radius: 20px;
- padding: .25rem .75rem;
+ background: $white;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
+ padding: .25rem .7rem;
color: $muted;
- font-weight: 500;
+ font-weight: 400;
}
}
}
@@ -384,23 +384,22 @@ body {
.var-name {
font-size: .82rem;
- font-weight: 600;
- color: $text-sm;
+ font-weight: 400;
+ color: $ink-soft;
min-width: 110px;
flex-shrink: 0;
}
.var-bar-track {
flex: 1;
- height: 10px;
- background: #F3F4F6;
- border-radius: 5px;
+ height: 8px;
+ background: $track;
+ border-radius: $radius;
overflow: hidden;
.var-bar-fill {
height: 100%;
- border-radius: 5px;
- transition: width .5s ease;
+ transition: width .4s ease;
}
}
@@ -409,39 +408,40 @@ body {
font-weight: 700;
min-width: 32px;
text-align: right;
- color: $text;
+ color: $ink;
+ font-variant-numeric: tabular-nums;
}
}
-// Score color via data-score attribute (set inline via JS)
-.var-bar-fill[data-level="low"] { background: linear-gradient(90deg, #EF4444, #F87171); }
-.var-bar-fill[data-level="mid"] { background: linear-gradient(90deg, #FBBF24, #FCD34D); }
-.var-bar-fill[data-level="high"] { background: linear-gradient(90deg, #22C55E, #4ADE80); }
+// 점수 수준은 색이 아니라 회색 농도로 구분
+.var-bar-fill[data-level="low"] { background: #C7C7C7; }
+.var-bar-fill[data-level="mid"] { background: #8A8A8A; }
+.var-bar-fill[data-level="high"] { background: $ink; }
.judgment-section {
margin-top: 1.5rem;
padding-top: 1.2rem;
- border-top: 1px solid $border;
+ border-top: 1px solid $line-soft;
.judgment-label {
font-size: .875rem;
- font-weight: 600;
+ font-weight: 700;
margin-bottom: .75rem;
- color: $text-sm;
+ color: $ink;
}
}
.confidence-section {
margin-top: 1.2rem;
padding: 1rem 1.2rem;
- background: #F9FAFB;
- border-radius: $radius-sm;
- border: 1px solid $border;
+ background: $white;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
.confidence-label {
font-size: .82rem;
- font-weight: 600;
- color: $text-sm;
+ font-weight: 700;
+ color: $ink;
margin-bottom: .6rem;
display: flex;
justify-content: space-between;
@@ -450,21 +450,27 @@ body {
.confidence-slider {
width: 100%;
appearance: none;
- height: 6px;
- border-radius: 3px;
- background: $border;
- outline: none;
+ height: 4px;
+ border-radius: $radius;
+ background: $track;
cursor: pointer;
&::-webkit-slider-thumb {
appearance: none;
- width: 20px;
- height: 20px;
+ width: 18px;
+ height: 18px;
border-radius: 50%;
- background: $primary;
+ background: $ink;
+ cursor: pointer;
+ border: none;
+ }
+ &::-moz-range-thumb {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: $ink;
+ border: none;
cursor: pointer;
- border: 2px solid #fff;
- box-shadow: 0 1px 4px rgba(0,0,0,.2);
}
}
@@ -481,7 +487,6 @@ body {
// ──────────────────────────────────────────
.judgment-likert-row {
display: flex;
- gap: .5rem;
margin-top: .5rem;
}
@@ -492,11 +497,12 @@ body {
align-items: center;
gap: .3rem;
padding: .7rem .3rem;
- border: 2px solid $border;
- border-radius: $radius-sm;
- background: $card-bg;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
+ background: $white;
cursor: pointer;
- transition: all .15s;
+
+ + .judgment-likert-btn { margin-left: -1px; }
.jl-num {
font-size: 1.1rem;
@@ -511,26 +517,17 @@ body {
line-height: 1.3;
}
- &:hover { border-color: $primary; }
+ &:hover { border-color: $line; position: relative; z-index: 1; }
- &.active {
- border-color: $primary;
- background: rgba($primary, .07);
- .jl-num { color: $primary; }
- .jl-label { color: $primary; }
+ // 값별 색상 구분 없이 선택만 검정 반전
+ &.active,
+ &[data-val].active {
+ border-color: $ink;
+ background: $ink;
+ position: relative;
+ z-index: 2;
+ .jl-num, .jl-label { color: $white; }
}
-
- // Color coding by value
- &[data-val="1"].active { border-color: $danger; background: rgba($danger,.07);
- .jl-num, .jl-label { color: $danger; } }
- &[data-val="2"].active { border-color: #F97316; background: rgba(#F97316,.07);
- .jl-num, .jl-label { color: #F97316; } }
- &[data-val="3"].active { border-color: $warning; background: rgba($warning,.07);
- .jl-num, .jl-label { color: $warning; } }
- &[data-val="4"].active { border-color: #22C55E; background: rgba(#22C55E,.07);
- .jl-num, .jl-label { color: #22C55E; } }
- &[data-val="5"].active { border-color: $success; background: rgba($success,.07);
- .jl-num, .jl-label { color: $success; } }
}
// ──────────────────────────────────────────
@@ -541,7 +538,7 @@ body {
font-size: .85rem;
color: $muted;
margin-bottom: 1.2rem;
- font-weight: 500;
+ font-weight: 400;
}
.pair-criteria {
@@ -555,27 +552,27 @@ body {
flex: 1;
text-align: center;
font-size: 1rem;
- font-weight: 700;
- color: $text;
+ font-weight: 400;
+ color: $muted;
padding: .6rem;
- border-radius: $radius-sm;
- transition: background .2s;
+ border-radius: $radius;
&.crit-left { text-align: right; }
&.crit-right { text-align: left; }
- &.crit-active-left { color: $primary; background: rgba($primary,.07); }
- &.crit-active-right { color: $success; background: rgba($success,.07); }
+ // 우세한 쪽은 색이 아니라 굵기/명도로 강조
+ &.crit-active-left,
+ &.crit-active-right { color: $ink; font-weight: 800; }
}
.vs-badge {
- font-size: .8rem;
+ font-size: .75rem;
font-weight: 700;
color: $muted;
- background: $bg;
- border: 1px solid $border;
- border-radius: 20px;
- padding: .25rem .6rem;
+ background: $white;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
+ padding: .2rem .5rem;
flex-shrink: 0;
}
}
@@ -589,39 +586,44 @@ body {
display: flex;
justify-content: space-between;
margin-bottom: .5rem;
- // Pad by half the thumb width (12px) so labels align with thumb at min/max
- padding: 0 12px;
+ // Pad by half the thumb width (11px) so labels align with thumb at min/max
+ padding: 0 11px;
span {
font-size: .7rem;
color: $muted;
- font-weight: 600;
+ font-weight: 400;
text-align: center;
min-width: 0;
}
- .sc-mid { font-weight: 800; color: $text-sm; font-size: .75rem; }
+ .sc-mid { font-weight: 800; color: $ink; font-size: .72rem; }
}
.ahp-slider {
width: 100%;
appearance: none;
- height: 8px;
- border-radius: 4px;
- background: linear-gradient(to right, rgba($primary,.15) 0%, $primary 50%, rgba($success,.15) 100%);
- outline: none;
+ height: 4px;
+ border-radius: $radius;
+ background: $track;
cursor: pointer;
&::-webkit-slider-thumb {
appearance: none;
- width: 24px;
- height: 24px;
+ width: 22px;
+ height: 22px;
border-radius: 50%;
- background: #fff;
- border: 3px solid $primary;
- box-shadow: 0 2px 6px rgba(0,0,0,.2);
+ background: $ink;
+ border: none;
+ cursor: pointer;
+ }
+ &::-moz-range-thumb {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: $ink;
+ border: none;
cursor: pointer;
- transition: border-color .2s;
}
}
@@ -629,25 +631,15 @@ body {
text-align: center;
margin-top: 1rem;
padding: .7rem 1rem;
- background: #F0F9FF;
- border: 1px solid #BAE6FD;
- border-radius: $radius-sm;
+ background: $white;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
font-size: .875rem;
- font-weight: 600;
- color: #0369A1;
+ font-weight: 700;
+ color: $ink;
min-height: 42px;
}
-.cr-warning {
- background: #FEF9C3;
- border: 1px solid #FDE047;
- border-radius: $radius-sm;
- padding: 1rem 1.2rem;
- margin-top: 1rem;
-
- p { font-size: .875rem; color: #713F12; margin-bottom: .75rem; font-weight: 600; }
-}
-
// ──────────────────────────────────────────
// Results (Step 4)
// ──────────────────────────────────────────
@@ -662,57 +654,64 @@ body {
.wc-label {
font-size: .82rem;
- font-weight: 600;
- color: $text-sm;
+ font-weight: 400;
+ color: $ink-soft;
min-width: 110px;
flex-shrink: 0;
}
.wc-track {
flex: 1;
- height: 18px;
- background: #F3F4F6;
- border-radius: 9px;
+ height: 14px;
+ background: $track;
+ border-radius: $radius;
overflow: hidden;
.wc-fill {
height: 100%;
- background: linear-gradient(90deg, $primary, #60A5FA);
- border-radius: 9px;
- transition: width .6s ease;
+ background: $ink;
+ transition: width .5s ease;
}
}
- .wc-pct { font-size: .82rem; font-weight: 700; color: $primary; min-width: 38px; text-align: right; }
+ .wc-pct {
+ font-size: .82rem;
+ font-weight: 700;
+ color: $ink;
+ min-width: 38px;
+ text-align: right;
+ font-variant-numeric: tabular-nums;
+ }
}
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
- gap: 1rem;
+ gap: .75rem;
margin-top: 1rem;
.stat-box {
- background: $bg;
- border: 1px solid $border;
- border-radius: $radius-sm;
+ background: $white;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
padding: 1rem;
text-align: center;
- .stat-val { font-size: 1.6rem; font-weight: 800; color: $primary; }
+ .stat-val { font-size: 1.6rem; font-weight: 800; color: $ink; font-variant-numeric: tabular-nums; }
.stat-label { font-size: .75rem; color: $muted; margin-top: .25rem; line-height: 1.3; }
- &.stat-warn .stat-val { color: $warning; }
- &.stat-ok .stat-val { color: $success; }
+ // 경고/정상 모두 색 대신 테두리 굵기로만 구분
+ &.stat-warn { border-color: $line; }
+ &.stat-ok { border-color: $line-soft; }
}
}
.comparison-table-wrap {
overflow-x: auto;
margin-top: 1rem;
- border-radius: $radius-sm;
- border: 1px solid $border;
+ border: 1px solid $line;
+ border-radius: $radius;
table {
width: 100%;
@@ -720,46 +719,49 @@ body {
font-size: .82rem;
th {
- background: #F9FAFB;
+ background: $white;
padding: .6rem .8rem;
text-align: center;
font-weight: 700;
- border-bottom: 1px solid $border;
+ border-bottom: 1px solid $line;
white-space: nowrap;
- color: $text-sm;
+ color: $ink;
}
td {
padding: .55rem .8rem;
text-align: center;
- border-bottom: 1px solid $border;
+ border-bottom: 1px solid $line-soft;
+ color: $ink-soft;
- &:first-child { font-weight: 700; }
+ &:first-child { font-weight: 700; color: $ink; }
}
tr:last-child td { border-bottom: none; }
- tr.mismatch td { background: #FEF2F2; color: $danger; }
- tr.mismatch td:first-child { color: $danger; }
+ // 불일치 행은 옅은 회색 면으로만 표시
+ tr.mismatch td { background: $wash; color: $ink; }
+ tr.mismatch td:first-child { color: $ink; }
}
}
.badge {
display: inline-block;
padding: .2rem .55rem;
- border-radius: 12px;
- font-size: .75rem;
+ border-radius: $radius;
+ font-size: .72rem;
font-weight: 700;
- line-height: 1;
+ line-height: 1.4;
+ border: 1px solid transparent;
- &.badge-select { background: #DBEAFE; color: $primary; }
- &.badge-reject { background: #F3F4F6; color: $muted; }
- &.badge-match { background: #DCFCE7; color: $success; }
- &.badge-mismatch { background: #FEE2E2; color: $danger; }
- &.badge-neutral { background: #FEF9C3; color: $warning; }
+ &.badge-select { background: $ink; color: $white; border-color: $ink; }
+ &.badge-reject { background: $white; color: $muted; border-color: $line-soft; }
+ &.badge-match { background: $white; color: $ink; border-color: $line; }
+ &.badge-mismatch { background: $ink; color: $white; border-color: $ink; }
+ &.badge-neutral { background: $white; color: $muted; border-color: $line-soft; }
}
-.neutral-row td { background: #FAFAF0; color: $warning; }
+.neutral-row td { background: $wash; color: $ink-soft; }
// ──────────────────────────────────────────
// Post Survey (Step 5)
@@ -769,15 +771,14 @@ body {
.likert-q {
font-size: .9rem;
- font-weight: 600;
- color: $text;
+ font-weight: 700;
+ color: $ink;
margin-bottom: .75rem;
line-height: 1.5;
}
.likert-options {
display: flex;
- gap: .5rem;
label {
flex: 1;
@@ -787,25 +788,26 @@ body {
gap: .3rem;
cursor: pointer;
+ + label .likert-btn { margin-left: -1px; }
+
input[type="radio"] { display: none; }
.likert-btn {
width: 100%;
padding: .55rem .2rem;
- border: 2px solid $border;
- border-radius: $radius-sm;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
text-align: center;
font-size: .9rem;
font-weight: 700;
color: $muted;
- transition: all .15s;
- background: $card-bg;
+ background: $white;
}
.likert-desc { font-size: .65rem; color: $muted; text-align: center; line-height: 1.2; }
- input:checked + .likert-btn { border-color: $primary; background: rgba($primary,.08); color: $primary; }
- &:hover .likert-btn { border-color: $primary; }
+ input:checked + .likert-btn { border-color: $ink; background: $ink; color: $white; position: relative; z-index: 1; }
+ &:hover .likert-btn { border-color: $line; }
}
}
}
@@ -813,15 +815,14 @@ body {
.open-textarea {
width: 100%;
padding: .8rem 1rem;
- border: 1.5px solid $border;
- border-radius: $radius-sm;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
font-size: .875rem;
font-family: inherit;
+ color: $ink;
+ background: $white;
resize: vertical;
min-height: 100px;
- outline: none;
- transition: border-color .2s;
- &:focus { border-color: $primary; }
}
// ──────────────────────────────────────────
@@ -831,15 +832,16 @@ body {
text-align: center;
padding: 2rem 1rem;
- .complete-icon { font-size: 3.5rem; margin-bottom: 1rem; }
- .complete-title { font-size: 1.5rem; font-weight: 800; margin-bottom: .5rem; }
+ .complete-icon { font-size: 2.5rem; margin-bottom: 1rem; filter: grayscale(1); }
+ .complete-title { font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em; margin-bottom: .5rem; }
.complete-desc { color: $muted; font-size: .9rem; margin-bottom: 2rem; line-height: 1.7; }
}
.json-output {
- background: #1E293B;
- color: #94A3B8;
- border-radius: $radius-sm;
+ background: $white;
+ color: $ink-soft;
+ border: 1px solid $line-soft;
+ border-radius: $radius;
padding: 1.2rem;
font-family: 'Menlo', 'Monaco', 'Consolas', monospace;
font-size: .75rem;
@@ -857,7 +859,7 @@ body {
// ──────────────────────────────────────────
.divider {
height: 1px;
- background: $border;
+ background: $line-soft;
margin: 1.5rem 0;
}
@@ -868,9 +870,10 @@ body {
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: .5rem; }
+// 파란색 강조 대신 굵기로만 강조
.highlight-text {
- color: $primary;
- font-weight: 700;
+ color: $ink;
+ font-weight: 800;
}
// ──────────────────────────────────────────
@@ -883,6 +886,8 @@ body {
.card { padding: 1.2rem; }
+ .step-instruction { font-size: .95rem; margin-bottom: 1.2rem; }
+
.step-progress .step-dot .dot-label { display: none; }
.var-row .var-name { min-width: 80px; font-size: .76rem; }
@@ -900,8 +905,6 @@ body {
.likert-group .likert-options label .likert-desc { display: none; }
.btn.btn-lg { padding: .75rem 1.5rem; font-size: .9rem; }
-
- .btn-row.judgment-btns { gap: .5rem; }
}
@media (max-width: 380px) {