diff --git a/app.vue b/app.vue
index e4394a3..6aade1c 100644
--- a/app.vue
+++ b/app.vue
@@ -9,12 +9,11 @@
-
+ class="flex items-center gap-2 hover:opacity-80 transition-opacity"
+ >
+ class="text-black dark:text-white text-center text-2xl font-mono uppercase font-bold"
+ >
Urgent Tools
@@ -25,49 +24,41 @@
+ aria-label="Main navigation"
+ >
- Co-Op in 6 Months
+ 'bg-neutral-100 dark:bg-neutral-800': isCoopBuilderSection,
+ }"
+ >
+ Co-Op Builder
+
+
+ 'bg-neutral-100 dark:bg-neutral-800': $route.path === '/wizards',
+ }"
+ >
Wizards
-
- Downloads
-
-
-
-
- {{ item.label }}
-
+
+
+
+
@@ -82,17 +73,20 @@
diff --git a/assets/css/main.css b/assets/css/main.css
index 0575114..546c443 100644
--- a/assets/css/main.css
+++ b/assets/css/main.css
@@ -1,29 +1,25 @@
-
+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Ubuntu:wght@300;400;500;700&family=Ubuntu+Mono:wght@400;700&display=swap");
@import "tailwindcss";
@import "@nuxt/ui";
-/* Ubuntu font import */
-@import url("https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&family=Ubuntu+Mono:wght@400;700&display=swap");
-
-[data-theme="dark"] {
- html { @apply bg-white text-neutral-900; }
- html.dark { @apply bg-neutral-950 text-neutral-100; }
+@theme {
+ --font-body: "Ubuntu", "Inter", sans-serif;
+ --font-mono: "Ubuntu Mono", monospace;
}
-/* Disable all animations, transitions, and smooth scrolling app-wide */
html,
body {
- scroll-behavior: auto !important;
+ @apply font-body bg-white text-neutral-900 dark:bg-neutral-950 dark:text-neutral-100;
}
-*,
-*::before,
-*::after {
- animation: none !important;
- transition: none !important;
+/* All headers use Inter font */
+h1, h2, h3, h4, h5, h6 {
+ font-family: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
+
+
/* =========================
TEMPLATE DOCUMENT LAYOUT
========================= */
@@ -47,7 +43,7 @@ body {
========================= */
.section-card {
- @apply border border-neutral-200 dark:border-neutral-800 rounded-lg p-5 mb-8;
+ @apply border border-neutral-200 dark:border-neutral-800 rounded-lg p-5 mb-8 relative;
}
.section-card::before {
@@ -57,27 +53,24 @@ body {
left: 4px;
right: -4px;
bottom: -4px;
- background: black;
+ @apply bg-black dark:bg-white;
background-image: radial-gradient(white 1px, transparent 1px);
background-size: 2px 2px;
z-index: -1;
}
+html.dark .section-card::before {
+ background-image: radial-gradient(black 1px, transparent 1px);
+}
+
.section-title {
- font-size: 1.75rem;
- font-weight: 800;
- color: inherit;
- margin: 0 0 1rem 0;
+ @apply text-3xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4;
+ font-family: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}
.subsection-title {
- font-size: 1.25rem;
- font-weight: 600;
- color: #374151;
- margin: 0 0 0.75rem 0;
- text-decoration: none;
- border-bottom: 1px solid #e5e7eb;
- padding-bottom: 0.25rem;
+ @apply text-xl font-semibold text-neutral-700 dark:text-neutral-300 mb-3 no-underline border-b border-neutral-200 dark:border-neutral-700 pb-1;
+ font-family: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}
/* =========================
@@ -121,55 +114,36 @@ body {
}
.form-group-large .large-field {
- @apply block w-full mt-2 text-lg rounded-md border-none transition-colors duration-150 ease-in-out;
+ @apply block w-full mt-2 text-lg rounded-md border-none transition-colors duration-150 ease-in-out bg-neutral-50 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100;
}
.form-group-large .large-field:focus {
- background: #f3f4f6;
+ @apply bg-neutral-100 dark:bg-neutral-700 outline-2 outline-blue-500 -outline-offset-2;
box-shadow: none;
- outline: 2px solid #3b82f6;
- outline-offset: -2px;
}
.inline-field {
- display: inline-block;
- margin: 0 0.25rem;
- min-width: 120px;
- border: none;
- background: #f9fafb;
- padding: 0.25rem 0.5rem;
- border-radius: 0.25rem;
+ @apply inline-block mx-1 min-w-[120px] border-none bg-neutral-50 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 px-2 py-1 rounded;
}
.inline-field:focus {
- background: #f3f4f6;
- outline: 1px solid #3b82f6;
- outline-offset: -1px;
+ @apply bg-neutral-100 dark:bg-neutral-700 outline-1 outline-blue-500 -outline-offset-1;
}
.number-field {
- min-width: 80px !important;
- text-align: center;
+ @apply min-w-[80px] text-center;
}
.wide-field {
- min-width: 250px !important;
+ @apply min-w-[250px];
}
.form-group-block .block-field {
- display: block;
- width: 100%;
- margin-top: 0.25rem;
- border: none;
- background: #f9fafb;
- padding: 0.5rem;
- border-radius: 0.25rem;
+ @apply block w-full mt-1 border-none bg-neutral-50 dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 p-2 rounded;
}
.form-group-block .block-field:focus {
- background: #f3f4f6;
- outline: 1px solid #3b82f6;
- outline-offset: -1px;
+ @apply bg-neutral-100 dark:bg-neutral-700 outline-1 outline-blue-500 -outline-offset-1;
}
/* =========================
@@ -263,16 +237,75 @@ body {
========================= */
.dither-shadow {
- background: black;
+ @apply bg-black dark:bg-white;
background-image: radial-gradient(white 1px, transparent 1px);
background-size: 2px 2px;
}
html.dark .dither-shadow {
- background: white;
background-image: radial-gradient(black 1px, transparent 1px);
}
+/* =========================
+ SELECTED ITEM PATTERN
+ ========================= */
+
+/* Pattern for selected items with dithered shadow and patterned background */
+.item-selected {
+ @apply relative bg-white dark:bg-neutral-950;
+}
+
+.item-selected::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-image: repeating-linear-gradient(
+ 45deg,
+ transparent 0px,
+ transparent 1px,
+ black 1px,
+ black 2px
+ );
+ opacity: 0.1;
+ pointer-events: none;
+ z-index: 0;
+}
+
+html.dark .item-selected::after {
+ background-image: repeating-linear-gradient(
+ 45deg,
+ transparent 0px,
+ transparent 1px,
+ white 1px,
+ white 2px
+ );
+}
+
+.item-selected > * {
+ position: relative;
+ z-index: 1;
+}
+
+/* Text background for better readability on selected items */
+.item-label-bg {
+ @apply bg-white/85 dark:bg-neutral-950/85 rounded;
+}
+
+.item-label-bg.selected {
+ @apply bg-white/95 dark:bg-neutral-950/95;
+}
+
+.item-text-bg {
+ @apply bg-white/90 dark:bg-neutral-950/90;
+}
+
+.item-text-bg.selected {
+ @apply bg-white/95 dark:bg-neutral-950/95;
+}
+
/* =========================
BUTTON STYLING
========================= */
@@ -330,123 +363,71 @@ html.dark .export-btn.primary:hover {
@apply bg-white text-black;
}
-/* General buttons with bitmap styling */
-button:not(.export-btn) {
- background: white !important;
- border: 1px solid black !important;
- color: black !important;
- font-family: "Ubuntu Mono", monospace !important;
- text-transform: uppercase !important;
- font-weight: bold !important;
- letter-spacing: 0.5px !important;
- cursor: pointer !important;
+/* Bitmap button base styling - more targeted approach */
+.bitmap-style {
+ @apply bg-white dark:bg-neutral-950 border border-black dark:border-white text-black dark:text-white font-mono uppercase font-bold tracking-wide cursor-pointer;
}
-button:not(.export-btn):hover {
- background: black !important;
- color: white !important;
- transform: translateY(-1px) translateX(-1px) !important;
+.bitmap-style:hover {
+ @apply bg-black dark:bg-white text-white dark:text-black;
+ transform: translateY(-1px) translateX(-1px);
}
-/* Dark mode buttons */
-html.dark button:not(.export-btn) {
- background: #0a0a0a !important;
- border: 1px solid white !important;
- color: white !important;
+/* Bitmap button styling for template cards */
+.bitmap-button {
+ @apply font-mono uppercase font-bold tracking-wider relative;
}
-html.dark button:not(.export-btn):hover {
- background: white !important;
- color: black !important;
+.bitmap-button:hover {
+ transform: translateY(-1px) translateX(-1px);
+ transition: transform 0.1s ease;
+}
+
+.bitmap-button:hover::after {
+ content: "";
+ position: absolute;
+ top: 1px;
+ left: 1px;
+ right: -1px;
+ bottom: -1px;
+ @apply border border-black dark:border-white bg-white dark:bg-neutral-950;
+ z-index: -1;
+}
+
+/* Constraint button selected styling */
+.constraint-selected {
+ @apply bg-black dark:bg-white text-white dark:text-black;
+}
+
+.constraint-selected:hover {
+ @apply bg-black dark:bg-white text-white dark:text-black;
}
/* =========================
BITMAP AESTHETIC OVERRIDES
========================= */
-/* Remove all rounded corners */
-* {
- border-radius: 0 !important;
- font-family: "Ubuntu", monospace !important;
+
+/* Bitmap form field styling - applied selectively */
+.bitmap-input {
+ @apply border border-black dark:border-white bg-white dark:bg-neutral-950 text-black dark:text-white font-mono;
}
-/* Form fields with bitmap styling */
-input,
-textarea,
-select {
- border: 1px solid black !important;
- background: white !important;
- color: black !important;
- font-family: "Ubuntu Mono", monospace !important;
+.bitmap-input:focus {
+ @apply outline-2 outline-black dark:outline-white -outline-offset-2 bg-white dark:bg-neutral-950;
}
-input:focus,
-textarea:focus,
-select:focus {
- outline: 2px solid black !important;
- outline-offset: -2px !important;
- background: white !important;
+/* Checkbox and radio button styling for bitmap theme */
+.bitmap-checkbox {
+ @apply border-2 border-black dark:border-white bg-white dark:bg-neutral-950;
}
-/* Dark mode form fields */
-html.dark input,
-html.dark textarea,
-html.dark select {
- border: 1px solid white !important;
- background: #0a0a0a !important;
- color: white !important;
+.bitmap-checkbox:checked {
+ @apply bg-black dark:bg-white text-white dark:text-black;
}
-html.dark input:focus,
-html.dark textarea:focus,
-html.dark select:focus {
- outline: 2px solid white !important;
- background: #0a0a0a !important;
-}
-/* Checkbox and radio button styling */
-input[type="checkbox"],
-input[type="radio"] {
- border: 2px solid black !important;
- background: white !important;
-}
-input[type="checkbox"]:checked,
-input[type="radio"]:checked {
- background: black !important;
- color: white !important;
-}
-
-html.dark input[type="checkbox"],
-html.dark input[type="radio"] {
- border: 2px solid white !important;
- background: #0a0a0a !important;
-}
-
-html.dark input[type="checkbox"]:checked,
-html.dark input[type="radio"]:checked {
- background: white !important;
- color: black !important;
-}
-
-/* Document titles */
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-family: "Ubuntu", monospace !important;
- color: inherit !important;
-}
-
-/* All text */
-p,
-span,
-div {
- color: inherit !important;
- font-family: "Ubuntu", monospace !important;
-}
/* =========================
HIDE ELEMENTS FROM PRINT
@@ -523,6 +504,85 @@ div {
}
}
+/* =========================
+ VALIDATION STYLES
+ ========================= */
+
+.validation-error {
+ @apply text-red-500 dark:text-red-400 text-sm font-medium mt-2 flex items-center gap-1;
+}
+
+/* =========================
+ TEMPLATE CARD STYLES
+ ========================= */
+
+.template-card {
+ @apply relative font-mono;
+}
+
+.help-section {
+ @apply relative;
+}
+
+.coming-soon {
+ @apply opacity-70;
+}
+
+.dither-tag {
+ @apply relative bg-white dark:bg-neutral-950;
+}
+
+.dither-tag::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-image: repeating-linear-gradient(
+ 45deg,
+ transparent 0px,
+ transparent 1px,
+ black 1px,
+ black 2px
+ );
+ opacity: 0.1;
+ pointer-events: none;
+}
+
+html.dark .dither-tag::before {
+ background-image: repeating-linear-gradient(
+ 45deg,
+ transparent 0px,
+ transparent 1px,
+ white 1px,
+ white 2px
+ );
+}
+
+.disabled-button {
+ @apply opacity-60 cursor-not-allowed;
+}
+
+/* =========================
+ ANIMATIONS
+ ========================= */
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.animate-fadeIn {
+ animation: fadeIn 0.5s ease-out;
+}
+
/* =========================
MOBILE RESPONSIVENESS
========================= */
diff --git a/components/BudgetCategorySelector.vue b/components/BudgetCategorySelector.vue
new file mode 100644
index 0000000..e1a68be
--- /dev/null
+++ b/components/BudgetCategorySelector.vue
@@ -0,0 +1,67 @@
+
+
+
+ {{ option }}
+
+
+
+
+
\ No newline at end of file
diff --git a/components/CoopBuilderSubnav.vue b/components/CoopBuilderSubnav.vue
new file mode 100644
index 0000000..c6c0d69
--- /dev/null
+++ b/components/CoopBuilderSubnav.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/ExportOptions.vue b/components/ExportOptions.vue
index b4860e9..abd25a6 100644
--- a/components/ExportOptions.vue
+++ b/components/ExportOptions.vue
@@ -2,9 +2,8 @@
-
Export Options:
-
Copy as Text
-
+
-
-
+
@@ -582,6 +581,7 @@ const downloadFile = (content: string, filename: string, type: string) => {
}
.export-buttons {
+ @apply font-mono;
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
diff --git a/components/WizardRevenueStep.vue b/components/WizardRevenueStep.vue
index 8dd4f2c..d645993 100644
--- a/components/WizardRevenueStep.vue
+++ b/components/WizardRevenueStep.vue
@@ -1,132 +1,137 @@
-
-
-
-
- Where will your money come from?
-
-
- Add sources like client work, grants, product sales, or donations.
-
-
-
-
-
- Export
-
-
-
- Add stream
-
-
+
+
+
+ Where will your money come from?
+
+
+ Add sources like client work, grants, product sales, or donations.
+
-
-
-
- No revenue streams yet
-
-
- Get started by adding your first revenue source.
-
-
-
- Add your first revenue stream
-
-
+
+
+
+
+
+
+ Export
+
+
-
-
-
-
-
+
+
+
+ No revenue streams yet
+
+
+ Get started by adding your first revenue source.
+
+
+
+ Add your first revenue stream
+
+
-
-
-
+
+
+
+
+
-
-
-
- $
-
-
-
+
+
+
+
+
+
+
+ $
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Add another stream
+
+
+
+
+
+
+ Add stream
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
- Add another stream
-
-
-
diff --git a/components/WizardReviewStep.vue b/components/WizardReviewStep.vue
index 2f8014d..758657e 100644
--- a/components/WizardReviewStep.vue
+++ b/components/WizardReviewStep.vue
@@ -1,20 +1,12 @@
-
-
-
-
Review & Complete
-
- Review your setup and complete the wizard to start using your co-op
- tool.
-
-
-
-
-
- Export All
-
-
+
+
+
Review & Complete
+
+ Review your setup and complete the wizard to start using your co-op
+ tool.
+
@@ -371,28 +363,4 @@ function completeSetup() {
}
}
-function exportSetup() {
- // Create export data
- const setupData = {
- members: members.value,
- policies: policies.value,
- overheadCosts: overheadCosts.value,
- streams: streams.value,
- exportedAt: new Date().toISOString(),
- version: "1.0",
- };
-
- // Download as JSON
- const blob = new Blob([JSON.stringify(setupData, null, 2)], {
- type: "application/json",
- });
- const url = URL.createObjectURL(blob);
- const a = document.createElement("a");
- a.href = url;
- a.download = `coop-setup-${new Date().toISOString().split("T")[0]}.json`;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
-}
diff --git a/components/WizardSubnav.vue b/components/WizardSubnav.vue
index 0b6068e..5d2716f 100644
--- a/components/WizardSubnav.vue
+++ b/components/WizardSubnav.vue
@@ -1,52 +1,22 @@
-
-
-
-
-
-
- Setup Wizard
-
-
-
-
-
-
+
+
+
-
+ ? 'bg-black text-white dark:bg-white dark:text-black no-underline'
+ : ''
+ "
+ >
{{ wizard.name }}
-
-
-
-
-
- All Wizards
-
@@ -55,30 +25,25 @@
diff --git a/pages/coach/skills-to-offers.vue b/pages/coach/skills-to-offers.vue
new file mode 100644
index 0000000..d50f9b7
--- /dev/null
+++ b/pages/coach/skills-to-offers.vue
@@ -0,0 +1,684 @@
+
+
+
+
+
+
+
+
+ Turn skills into fair, sellable offers
+
+
+ Tell us what you're good at and who you help. We'll suggest offers that match your co-op's shared capacity.
+
+
+
+
+ Skip coach → Streams
+
+
+
+
+
+
+ Load sample data
+
+
+
+
+
+
+
+
+
+
+ A) Name your strengths
+
+
+
+
+
+
+
+
+ Focus keeps offers shippable
+
+
+
+
+
+ Pick what you can reliably do as a team. We'll keep it simple.
+
+
+
+
+
+
+
{{ member.name }}
+
{{ member.role }}
+
+
+ {{ getSelectedSkillsCount(member.id) }}/3 skills selected
+
+
+
+
+
+ {{ skill.label }}
+
+
+
+
+
+
+
+
+
+
+ B) Who do you help?
+
+
+
+
+
+
+
+
+ Focus keeps offers shippable
+
+
+
+
+
+ Choose the problems you can solve this month. We'll suggest time-boxed offers.
+
+
+
+
+
+ {{ problem.label }}
+
+
+
+
+ see examples
+
+
+
+
+
+
Examples:
+
+
+ •
+ {{ example }}
+
+
+
+
+ Close
+
+
+
+
+
+
+ {{ selectedProblems.length }}/2 problem types selected
+
+
+
+
+
+
+ C) Suggested offers
+
+
+
+
+
+
+
Generating offers...
+
+ Creating personalized revenue suggestions based on your selections.
+
+
+
+
+
+
+
+
+
No offers yet
+
+ Pick a few skills and a problem—we'll suggest something you can sell this month.
+
+
+ We need at least one shared skill and one problem type to suggest offers.
+
+
+
+
+
+
+
+
{{ offer.name }}
+
+
+
+
+ Covers ~{{ calculateMonthlyCoverage(offer) }}% of monthly needs at baseline
+
+
+ Typical payout: {{ getPayoutDaysRange(offer) }}
+
+
+ Why this
+
+
+
+
+
+
+
+
+
+ Baseline:
+ ${{ offer.price.baseline.toLocaleString() }}
+
+
+ Stretch:
+ ${{ offer.price.stretch.toLocaleString() }}
+
+
{{ offer.price.calcNote }}
+
+
+
+
+ Payment timing:
+ {{ offer.payoutDelayDays }} days
+
+
+
+
+
Why this works for your co-op:
+
+
+ ✓
+ {{ updateLanguageToCoopTerms(reason) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ← Back
+
+
+
+
+ 🔄 Regenerate
+
+
+
+ Add to plan
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/coop-builder.vue b/pages/coop-builder.vue
new file mode 100644
index 0000000..98975a4
--- /dev/null
+++ b/pages/coop-builder.vue
@@ -0,0 +1,468 @@
+
+
+
+
+
+
+
+
+ Co-op Builder
+
+
+
+
+
+
+
+
+
+
+
+
+
+ You're all set!
+
+
+ Your co-op is configured and ready to go.
+
+
+
+
+ Start Over
+
+
+ Go to Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+ Add your team
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+ Set your wage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Monthly costs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+
+
+
+ Revenue streams
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+ Review & finish
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start Over
+
+
+
+
+
+
+
+ Saving...
+ Saved
+
+
+
+ Complete Setup
+
+
+
+
+
+
+
+
+
diff --git a/pages/coop-planner.vue b/pages/coop-planner.vue
index 0fe9eaf..b74822e 100644
--- a/pages/coop-planner.vue
+++ b/pages/coop-planner.vue
@@ -1,8 +1,8 @@
-
+
diff --git a/pages/scenarios.vue b/pages/scenarios.vue
index 6e5ad9b..8ff5c01 100644
--- a/pages/scenarios.vue
+++ b/pages/scenarios.vue
@@ -344,7 +344,7 @@ const streamsStore = useStreamsStore();
const budgetStore = useBudgetStore();
const cashStore = useCashStore();
const sessionStore = useSessionStore();
-const wizardStore = useWizardStore();
+const coopBuilderStore = useCoopBuilderStore();
const isResetting = ref(false);
@@ -556,7 +556,7 @@ async function restartWizard() {
sessionStore.resetSession();
// Reset wizard state
- wizardStore.reset();
+ coopBuilderStore.reset();
// Small delay for UX
await new Promise((resolve) => setTimeout(resolve, 300));
diff --git a/pages/templates/conflict-resolution-framework.vue b/pages/templates/conflict-resolution-framework.vue
index 450dc98..a6bff90 100644
--- a/pages/templates/conflict-resolution-framework.vue
+++ b/pages/templates/conflict-resolution-framework.vue
@@ -1,19 +1,14 @@
-
-
-
+ title="Conflict Resolution" />
+ class="template-wrapper bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100">
@@ -21,9 +16,8 @@
- CONFLICT RESOLUTION FRAMEWORK
+ :data-org-name="formData.orgName || 'Organization'">
+ CONFLICT RESOLUTION
@@ -39,8 +33,7 @@
size="xl"
class="w-full"
:error="validationErrors.orgName"
- @input="debouncedAutoSave"
- />
+ @input="debouncedAutoSave" />
@@ -51,11 +44,12 @@
size="xl"
class="w-full"
:error="validationErrors.orgType"
- @change="autoSave"
- />
+ @change="autoSave" />
-
+
+ @change="autoSave" />
@@ -81,44 +74,51 @@
label="Include this section"
:ui="{
label: 'text-xs text-neutral-700 dark:text-neutral-300',
- }"
- />
+ }" />
+ class="form-group-large">
-
+ class="relative">
+
+
+
+
+
+
+ class="form-group-large">
+ @input="debouncedAutoSave" />
@@ -134,14 +134,25 @@
-
+ class="relative">
+
+
+
+
+
+
@@ -154,14 +165,14 @@
4. Primary Resolution Approach
-
+
+ class="mt-2" />
{{ validationErrors.approach }}
@@ -173,8 +184,7 @@
id="anonymous-reporting"
label="Allow anonymous reporting"
help="Members can report issues without revealing their identity"
- @change="autoSave"
- />
+ @change="autoSave" />
@@ -186,36 +196,49 @@
+ class="form-group-large">
-
+ class="relative">
+
+
+
+
+
+
-
+
{{ validationErrors.reportReceivers }}
-
+
+ @change="autoSave" />
@@ -224,8 +247,7 @@
id="support-people"
label="Allow support people in mediation sessions"
help="Parties can bring a trusted person for emotional support"
- @change="autoSave"
- />
+ @change="autoSave" />
@@ -236,7 +258,9 @@
-
+
+ @change="autoSave" />
-
+
+ @change="autoSave" />
+ class="form-group-large">
-
+ class="relative">
+
+
+
+
+
+
-
+
{{ validationErrors.processSteps }}
@@ -288,7 +324,8 @@
-
@@ -345,44 +382,55 @@
+ class="form-group-large">
-
+ class="relative">
+
+
+
+
+
+
-
+
{{ validationErrors.availableActions }}
+ class="form-group-large border-t border-neutral-200 dark:border-neutral-800 pt-4">
+ @change="autoSave" />
-
@@ -419,19 +477,22 @@
10. Implementation Details
-
+
+ @input="debouncedAutoSave" />
-
+
+ @change="autoSave" />
+ class="form-group-large">
+ @change="autoSave" />
@@ -466,8 +524,7 @@
type="date"
size="xl"
class="w-full mb-0"
- @change="autoSave"
- />
+ @change="autoSave" />
@@ -476,8 +533,7 @@
type="date"
size="xl"
class="w-full mb-0"
- @change="autoSave"
- />
+ @change="autoSave" />
@@ -486,7 +542,8 @@
-
-
-
13. Responsible Contact People Structure
+
+ 13. Responsible Contact People Structure
+
-
+
+ @change="autoSave" />
+ class="form-group-large">
+ @input="debouncedAutoSave" />
+ class="form-group-large">
+ @change="autoSave" />
@@ -638,19 +701,32 @@
14. Formal Complaint Requirements
-
+
-
+ class="relative">
+
+
+
+
+
+
@@ -658,30 +734,26 @@
+ class="form-group-large">
+ @change="autoSave" />
+ class="form-group-large">
+ @change="autoSave" />
@@ -691,8 +763,7 @@
id="require-external-advice"
label="Require external legal advice for complex complaints"
help="Seek external expertise for multi-party or staff/director complaints"
- @change="autoSave"
- />
+ @change="autoSave" />
@@ -708,36 +779,31 @@
id="minutes-of-settlement"
label="Require 'Minutes of Settlement' for resolved complaints"
help="Agreements must be documented in writing and signed by both parties"
- @change="autoSave"
- />
+ @change="autoSave" />
+ class="form-group-large">
+ @change="autoSave" />
+ class="form-group-large">
+ @change="autoSave" />
@@ -745,7 +811,8 @@
-
@@ -797,23 +864,20 @@
+ class="border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-950 p-5 rounded-lg shadow-sm">
📄 Policy Document Preview
+ title="Hide preview">
✕
+ v-html="markdownToHtml(generatePolicyDocument())">
@@ -821,11 +885,10 @@
-
+
@@ -903,7 +966,13 @@ const responseTimeOptions = [
"Within 2 weeks",
];
-const resolutionTimeOptions = ["1 week", "2 weeks", "30 days", "60 days", "90 days"];
+const resolutionTimeOptions = [
+ "1 week",
+ "2 weeks",
+ "30 days",
+ "60 days",
+ "90 days",
+];
const docLevelOptions = [
"Minimal - outcomes only",
@@ -964,7 +1033,13 @@ const acknowledgmentTimeOptions = [
"Within 2 weeks",
];
-const reviewTimeOptions = ["2 weeks", "1 month", "6 weeks", "2 months", "3 months"];
+const reviewTimeOptions = [
+ "2 weeks",
+ "1 month",
+ "6 weeks",
+ "2 months",
+ "3 months",
+];
const sectionsEnabled = ref({
values: true,
@@ -1138,14 +1213,18 @@ const validateForm = () => {
}
// Required checkbox groups (must have at least one checked)
- const checkedConflictTypes = conflictTypes.value.filter((item) => item.checked);
+ const checkedConflictTypes = conflictTypes.value.filter(
+ (item) => item.checked
+ );
if (checkedConflictTypes.length === 0) {
errors.conflictTypes = "Please select at least one type of conflict";
}
// Note: Guiding Principles & Values section is optional - no validation needed
- const checkedReportReceivers = reportReceivers.value.filter((item) => item.checked);
+ const checkedReportReceivers = reportReceivers.value.filter(
+ (item) => item.checked
+ );
if (checkedReportReceivers.length === 0) {
errors.reportReceivers = "Please select at least one report receiver";
}
@@ -1155,7 +1234,9 @@ const validateForm = () => {
errors.processSteps = "Please select at least one process step";
}
- const checkedAvailableActions = availableActions.value.filter((item) => item.checked);
+ const checkedAvailableActions = availableActions.value.filter(
+ (item) => item.checked
+ );
if (checkedAvailableActions.length === 0) {
errors.availableActions = "Please select at least one available action";
}
@@ -1203,8 +1284,9 @@ const completionPercentage = computed(() => {
...specialCircumstances.value,
];
- const filledInputs = allInputs.filter((val) => val && val.toString().trim() !== "")
- .length;
+ const filledInputs = allInputs.filter(
+ (val) => val && val.toString().trim() !== ""
+ ).length;
const checkedBoxes = checkboxInputs.filter((item) => item.checked).length;
const totalFields = allInputs.length + checkboxInputs.length;
@@ -1228,10 +1310,12 @@ const loadSavedData = () => {
// Load checkbox arrays
if (parsedData.coreValues) coreValues.value = parsedData.coreValues;
- if (parsedData.conflictTypes) conflictTypes.value = parsedData.conflictTypes;
+ if (parsedData.conflictTypes)
+ conflictTypes.value = parsedData.conflictTypes;
if (parsedData.reportReceivers)
reportReceivers.value = parsedData.reportReceivers;
- if (parsedData.processSteps) processSteps.value = parsedData.processSteps;
+ if (parsedData.processSteps)
+ processSteps.value = parsedData.processSteps;
if (parsedData.availableActions)
availableActions.value = parsedData.availableActions;
if (parsedData.specialCircumstances)
@@ -1498,18 +1582,9 @@ const exportData = computed(() => ({
transform: translateX(24px);
}
-/* Validation error styling */
-.validation-error {
- color: #ef4444;
- font-size: 0.875rem;
- font-weight: 500;
- margin-top: 0.5rem;
- display: flex;
- align-items: center;
- gap: 0.25rem;
-}
+/* Moved validation-error to main.css for consistency */
-/* Preview styling */
+/* Preview styling - template-specific */
.preview-controls {
text-align: center;
margin: 2rem 0;
@@ -1619,8 +1694,8 @@ html.dark .policy-preview blockquote {
}
.policy-preview code {
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
- "Courier New", monospace !important;
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
+ "Liberation Mono", "Courier New", monospace !important;
background: #f3f4f6;
padding: 0.1rem 0.35rem;
}
diff --git a/pages/templates/decision-framework.vue b/pages/templates/decision-framework.vue
index ac16d47..fe1c08f 100644
--- a/pages/templates/decision-framework.vue
+++ b/pages/templates/decision-framework.vue
@@ -1,365 +1,475 @@
-
-
-
+ title="Decision Framework Helper" />
-
-
+ class="template-wrapper bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100">
+
+
+
+
+
+
+ Decision Framework
+
+
-