799 lines
22 KiB
Vue
799 lines
22 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Loading State -->
|
|
<div v-if="loading" class="loading-state">
|
|
<p style="color: var(--text-faint)">Loading your profile...</p>
|
|
</div>
|
|
|
|
<!-- Unauthenticated State -->
|
|
<div v-else-if="!memberData" class="loading-state">
|
|
<p style="color: var(--text-faint); margin-bottom: 12px;">Please sign in to access your profile settings.</p>
|
|
<button
|
|
class="btn btn-primary"
|
|
@click="openLoginModal({ title: 'Sign in to your profile', description: 'Enter your email to manage your profile settings' })"
|
|
>
|
|
Sign In
|
|
</button>
|
|
</div>
|
|
|
|
<div v-else>
|
|
<!-- PAGE HEADER -->
|
|
<PageHeader
|
|
title="Edit Profile"
|
|
subtitle="How you appear to other members"
|
|
/>
|
|
|
|
<!-- TWO-COLUMN FORM -->
|
|
<form class="page-content" @submit.prevent="handleSubmit">
|
|
<div class="profile-columns">
|
|
|
|
<!-- ======== LEFT COLUMN ======== -->
|
|
<div class="profile-col-left">
|
|
|
|
<div class="section-label">Basics</div>
|
|
|
|
<div class="field">
|
|
<label>Name</label>
|
|
<input v-model="formData.name" type="text" placeholder="Your name" required />
|
|
</div>
|
|
|
|
<div class="row-2">
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>Pronouns</label>
|
|
<PrivacyToggle v-model="formData.pronounsPrivacy" />
|
|
</div>
|
|
<input v-model="formData.pronouns" type="text" placeholder="e.g., she/her, they/them" />
|
|
</div>
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>Timezone</label>
|
|
<PrivacyToggle v-model="formData.timeZonePrivacy" />
|
|
</div>
|
|
<input v-model="formData.timeZone" type="text" placeholder="e.g., America/Toronto" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>Avatar</label>
|
|
<PrivacyToggle v-model="formData.avatarPrivacy" />
|
|
</div>
|
|
<div class="avatar-row">
|
|
<button
|
|
v-for="ghost in availableGhosts"
|
|
:key="ghost.value"
|
|
type="button"
|
|
class="avatar-option"
|
|
:class="{ selected: formData.avatar === ghost.value }"
|
|
:title="ghost.label"
|
|
@click="formData.avatar = ghost.value"
|
|
>
|
|
<img :src="ghost.image" :alt="ghost.label" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- About You -->
|
|
<hr class="section-divider section-divider-left" />
|
|
<div class="section-label">About You</div>
|
|
|
|
<div class="row-2">
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>Studio / Organization</label>
|
|
<PrivacyToggle v-model="formData.studioPrivacy" />
|
|
</div>
|
|
<input v-model="formData.studio" type="text" placeholder="Studio name" />
|
|
</div>
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>Location</label>
|
|
<PrivacyToggle v-model="formData.locationPrivacy" />
|
|
</div>
|
|
<input v-model="formData.location" type="text" placeholder="Toronto, ON" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>Bio</label>
|
|
<PrivacyToggle v-model="formData.bioPrivacy" />
|
|
</div>
|
|
<textarea v-model="formData.bio" rows="2" placeholder="Share your background, interests, and experience..." maxlength="300"></textarea>
|
|
<div class="char-count">{{ formData.bio?.length || 0 }} / 300</div>
|
|
</div>
|
|
|
|
<!-- Skills Exchange -->
|
|
<hr class="section-divider section-divider-left" />
|
|
<div class="section-label">Skills Exchange</div>
|
|
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>What I Can Contribute</label>
|
|
<PrivacyToggle v-model="formData.offeringPrivacy" />
|
|
</div>
|
|
<TagInput v-model="formData.offering.tags" placeholder="add skill..." />
|
|
</div>
|
|
<div class="field">
|
|
<label>Details</label>
|
|
<textarea v-model="formData.offering.text" rows="2" placeholder="e.g., I have 10+ years in Unity and love helping new devs."></textarea>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<div class="label-row">
|
|
<label>What I'm Looking For</label>
|
|
<PrivacyToggle v-model="formData.lookingForPrivacy" />
|
|
</div>
|
|
<TagInput v-model="formData.lookingFor.tags" placeholder="add topic..." />
|
|
</div>
|
|
<div class="field">
|
|
<label>Details</label>
|
|
<textarea v-model="formData.lookingFor.text" rows="2" placeholder="e.g., Seeking a business-minded co-founder for a worker co-op studio."></textarea>
|
|
</div>
|
|
|
|
<!-- Visibility -->
|
|
<hr class="section-divider section-divider-left" />
|
|
<div class="section-label">Visibility</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.showInDirectory" />
|
|
<div class="toggle-label">
|
|
Show in Member Directory
|
|
<span class="toggle-sub">Your profile will appear in the public member listing</span>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- ======== RIGHT COLUMN ======== -->
|
|
<div class="profile-col-right">
|
|
|
|
<div class="section-label">Peer Support</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.peerSupportEnabled" />
|
|
<div class="toggle-label">
|
|
Offer Peer Support
|
|
<span class="toggle-sub">Let other members request 1:1 time with you</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="formData.peerSupportEnabled" class="peer-panel">
|
|
<div class="field">
|
|
<label>Skill-Based Topics</label>
|
|
<TagInput v-model="formData.peerSupportSkillTopics" placeholder="add topic..." />
|
|
<div v-if="suggestedSkillTopics.length" class="suggested">
|
|
Suggested from your offerings:
|
|
<a
|
|
v-for="tag in suggestedSkillTopics"
|
|
:key="tag"
|
|
@click="addSuggestedSkillTopic(tag)"
|
|
>{{ tag }}</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Conversational Topics</label>
|
|
<div class="checkbox-grid">
|
|
<label
|
|
v-for="topic in availableSupportTopics"
|
|
:key="topic"
|
|
class="checkbox-item"
|
|
:class="{ checked: formData.peerSupportSupportTopics.includes(topic) }"
|
|
@click.prevent="toggleSupportTopic(topic)"
|
|
>
|
|
<span class="cb">✓</span>
|
|
{{ topic }}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Availability</label>
|
|
<textarea v-model="formData.peerSupportAvailability" rows="2" placeholder="e.g. Weekday afternoons ET"></textarea>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Slack Handle</label>
|
|
<input v-model="formData.peerSupportSlackUsername" type="text" placeholder="@yourslackname" />
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Personal Message</label>
|
|
<textarea v-model="formData.peerSupportMessage" rows="2" maxlength="200" placeholder="Brief note shown to people requesting time with you"></textarea>
|
|
<div class="char-count">{{ formData.peerSupportMessage?.length || 0 }} / 200</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notifications -->
|
|
<hr class="section-divider section-divider-right" />
|
|
<div class="section-label">Notifications</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.notifyEvents" />
|
|
<div class="toggle-label">
|
|
Event reminders
|
|
<span class="toggle-sub">Get notified about upcoming events</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.notifyUpdates" />
|
|
<div class="toggle-label">
|
|
Community updates
|
|
<span class="toggle-sub">New posts from members you follow</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.notifyPeerRequests" />
|
|
<div class="toggle-label">
|
|
Peer support requests
|
|
<span class="toggle-sub">When someone wants to connect</span>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- ======== SAVE BAR ======== -->
|
|
<div class="save-bar">
|
|
<button type="submit" class="btn btn-primary" :disabled="saving || !hasChanges">
|
|
{{ saving ? 'Saving...' : 'Save Profile' }}
|
|
</button>
|
|
<button type="button" class="btn" @click="resetForm">Reset Changes</button>
|
|
<span v-if="saveSuccess" class="save-msg save-msg-ok">Profile updated.</span>
|
|
<span v-if="saveError" class="save-msg save-msg-err">{{ saveError }}</span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const { memberData, checkMemberStatus } = useAuth()
|
|
const { openLoginModal } = useLoginModal()
|
|
|
|
// Available ghost avatars
|
|
const availableGhosts = [
|
|
{ value: 'disbelieving', label: 'Disbelieving', image: '/ghosties/Ghost-Disbelieving.png' },
|
|
{ value: 'double-take', label: 'Double Take', image: '/ghosties/Ghost-Double-Take.png' },
|
|
{ value: 'exasperated', label: 'Exasperated', image: '/ghosties/Ghost-Exasperated.png' },
|
|
{ value: 'mild', label: 'Mild', image: '/ghosties/Ghost-Mild.png' },
|
|
{ value: 'sweet', label: 'Sweet', image: '/ghosties/Ghost-Sweet.png' },
|
|
{ value: 'wtf', label: 'WTF', image: '/ghosties/Ghost-WTF.png' },
|
|
]
|
|
|
|
// Form state
|
|
const formData = reactive({
|
|
name: '',
|
|
pronouns: '',
|
|
timeZone: '',
|
|
avatar: '',
|
|
studio: '',
|
|
bio: '',
|
|
location: '',
|
|
offering: { text: '', tags: [] },
|
|
lookingFor: { text: '', tags: [] },
|
|
showInDirectory: true,
|
|
// Peer support
|
|
peerSupportEnabled: false,
|
|
peerSupportSkillTopics: [],
|
|
peerSupportSupportTopics: [],
|
|
peerSupportAvailability: '',
|
|
peerSupportMessage: '',
|
|
peerSupportSlackUsername: '',
|
|
// Privacy
|
|
pronounsPrivacy: 'members',
|
|
timeZonePrivacy: 'members',
|
|
avatarPrivacy: 'members',
|
|
studioPrivacy: 'members',
|
|
bioPrivacy: 'members',
|
|
locationPrivacy: 'members',
|
|
offeringPrivacy: 'members',
|
|
lookingForPrivacy: 'members',
|
|
// Notifications
|
|
notifyEvents: true,
|
|
notifyUpdates: true,
|
|
notifyPeerRequests: true,
|
|
})
|
|
|
|
const loading = ref(false)
|
|
const saving = ref(false)
|
|
const saveSuccess = ref(false)
|
|
const saveError = ref(null)
|
|
const initialData = ref(null)
|
|
|
|
// Available conversational support topics
|
|
const availableSupportTopics = [
|
|
'Co-founder relationships',
|
|
'Burnout prevention',
|
|
'Impostor syndrome',
|
|
'Work-life boundaries',
|
|
'Conflict resolution',
|
|
'General chat & support',
|
|
]
|
|
|
|
// Computed
|
|
const hasChanges = computed(() => {
|
|
return JSON.stringify(formData) !== JSON.stringify(initialData.value)
|
|
})
|
|
|
|
const suggestedSkillTopics = computed(() => {
|
|
if (!formData.offering.tags?.length) return []
|
|
return formData.offering.tags.filter(
|
|
(t) => !formData.peerSupportSkillTopics?.includes(t)
|
|
)
|
|
})
|
|
|
|
// Toggle a support topic in/out of the selection
|
|
const toggleSupportTopic = (topic) => {
|
|
const idx = formData.peerSupportSupportTopics.indexOf(topic)
|
|
if (idx >= 0) {
|
|
formData.peerSupportSupportTopics.splice(idx, 1)
|
|
} else {
|
|
formData.peerSupportSupportTopics.push(topic)
|
|
}
|
|
}
|
|
|
|
const addSuggestedSkillTopic = (tag) => {
|
|
if (!Array.isArray(formData.peerSupportSkillTopics)) {
|
|
formData.peerSupportSkillTopics = []
|
|
}
|
|
if (!formData.peerSupportSkillTopics.includes(tag)) {
|
|
formData.peerSupportSkillTopics.push(tag)
|
|
}
|
|
}
|
|
|
|
// Load member data into form
|
|
const loadProfile = () => {
|
|
if (memberData.value) {
|
|
formData.name = memberData.value.name || ''
|
|
formData.pronouns = memberData.value.pronouns || ''
|
|
formData.timeZone = memberData.value.timeZone || ''
|
|
formData.avatar = memberData.value.avatar || ''
|
|
formData.studio = memberData.value.studio || ''
|
|
formData.bio = memberData.value.bio || ''
|
|
formData.location = memberData.value.location || ''
|
|
|
|
// Load offering (handle both old string and new object format)
|
|
if (typeof memberData.value.offering === 'string') {
|
|
formData.offering.text = memberData.value.offering
|
|
formData.offering.tags = []
|
|
} else if (memberData.value.offering) {
|
|
formData.offering.text = memberData.value.offering?.text || ''
|
|
formData.offering.tags = Array.isArray(memberData.value.offering?.tags)
|
|
? [...memberData.value.offering.tags]
|
|
: []
|
|
} else {
|
|
formData.offering.text = ''
|
|
formData.offering.tags = []
|
|
}
|
|
|
|
// Load lookingFor (handle both old string and new object format)
|
|
if (typeof memberData.value.lookingFor === 'string') {
|
|
formData.lookingFor.text = memberData.value.lookingFor
|
|
formData.lookingFor.tags = []
|
|
} else if (memberData.value.lookingFor) {
|
|
formData.lookingFor.text = memberData.value.lookingFor?.text || ''
|
|
formData.lookingFor.tags = Array.isArray(memberData.value.lookingFor?.tags)
|
|
? [...memberData.value.lookingFor.tags]
|
|
: []
|
|
} else {
|
|
formData.lookingFor.text = ''
|
|
formData.lookingFor.tags = []
|
|
}
|
|
|
|
formData.showInDirectory = memberData.value.showInDirectory ?? true
|
|
|
|
// Load peer support data
|
|
if (memberData.value.peerSupport) {
|
|
formData.peerSupportEnabled = memberData.value.peerSupport.enabled || false
|
|
formData.peerSupportSkillTopics = Array.isArray(memberData.value.peerSupport.skillTopics)
|
|
? [...memberData.value.peerSupport.skillTopics]
|
|
: []
|
|
formData.peerSupportSupportTopics = Array.isArray(memberData.value.peerSupport.supportTopics)
|
|
? [...memberData.value.peerSupport.supportTopics]
|
|
: []
|
|
formData.peerSupportAvailability = memberData.value.peerSupport.availability || ''
|
|
formData.peerSupportMessage = memberData.value.peerSupport.personalMessage || ''
|
|
formData.peerSupportSlackUsername = memberData.value.peerSupport.slackUsername || ''
|
|
} else {
|
|
formData.peerSupportEnabled = false
|
|
formData.peerSupportSkillTopics = []
|
|
formData.peerSupportSupportTopics = []
|
|
formData.peerSupportAvailability = ''
|
|
formData.peerSupportMessage = ''
|
|
formData.peerSupportSlackUsername = ''
|
|
}
|
|
|
|
// Load privacy settings (with defaults)
|
|
const privacy = memberData.value.privacy || {}
|
|
formData.pronounsPrivacy = privacy.pronouns || 'members'
|
|
formData.timeZonePrivacy = privacy.timeZone || 'members'
|
|
formData.avatarPrivacy = privacy.avatar || 'members'
|
|
formData.studioPrivacy = privacy.studio || 'members'
|
|
formData.bioPrivacy = privacy.bio || 'members'
|
|
formData.locationPrivacy = privacy.location || 'members'
|
|
formData.offeringPrivacy = privacy.offering || 'members'
|
|
formData.lookingForPrivacy = privacy.lookingFor || 'members'
|
|
|
|
// Load notification prefs
|
|
const notifs = memberData.value.notifications || {}
|
|
formData.notifyEvents = notifs.events ?? true
|
|
formData.notifyUpdates = notifs.updates ?? true
|
|
formData.notifyPeerRequests = notifs.peerRequests ?? true
|
|
|
|
// Store initial state for change detection
|
|
initialData.value = JSON.parse(JSON.stringify(formData))
|
|
}
|
|
}
|
|
|
|
// Handle form submission
|
|
const handleSubmit = async () => {
|
|
saving.value = true
|
|
saveSuccess.value = false
|
|
saveError.value = null
|
|
|
|
try {
|
|
// Save profile data
|
|
await $fetch('/api/members/profile', {
|
|
method: 'PATCH',
|
|
body: formData,
|
|
})
|
|
|
|
// Save peer support data separately
|
|
await $fetch('/api/members/me/peer-support', {
|
|
method: 'PATCH',
|
|
body: {
|
|
enabled: formData.peerSupportEnabled,
|
|
skillTopics: formData.peerSupportSkillTopics,
|
|
supportTopics: formData.peerSupportSupportTopics,
|
|
availability: formData.peerSupportAvailability,
|
|
personalMessage: formData.peerSupportMessage,
|
|
slackUsername: formData.peerSupportSlackUsername,
|
|
},
|
|
})
|
|
|
|
saveSuccess.value = true
|
|
|
|
// Refresh member data
|
|
await checkMemberStatus()
|
|
loadProfile()
|
|
|
|
setTimeout(() => { saveSuccess.value = false }, 3000)
|
|
} catch (error) {
|
|
console.error('Profile save error:', error)
|
|
saveError.value = error.data?.message || 'Failed to save profile. Please try again.'
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
// Reset form to initial state
|
|
const resetForm = () => {
|
|
loadProfile()
|
|
saveSuccess.value = false
|
|
saveError.value = null
|
|
}
|
|
|
|
// Initialize on mount
|
|
onMounted(async () => {
|
|
if (!memberData.value) {
|
|
loading.value = true
|
|
const isAuthenticated = await checkMemberStatus()
|
|
loading.value = false
|
|
|
|
if (!isAuthenticated) {
|
|
openLoginModal({
|
|
title: 'Sign in to your profile',
|
|
description: 'Enter your email to manage your profile settings',
|
|
})
|
|
return
|
|
}
|
|
}
|
|
|
|
loadProfile()
|
|
})
|
|
|
|
useHead({
|
|
title: 'Edit Profile - Ghost Guild',
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* ---- LOADING / EMPTY STATE ---- */
|
|
.loading-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 80px 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* ---- CONTENT AREA ---- */
|
|
.page-content {
|
|
padding: 0 28px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* ---- TWO-COLUMN LAYOUT ---- */
|
|
.profile-columns {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.profile-col-left {
|
|
padding-right: 24px;
|
|
padding-top: 14px;
|
|
border-right: 1px dashed var(--border);
|
|
}
|
|
|
|
.profile-col-right {
|
|
padding-left: 24px;
|
|
padding-top: 14px;
|
|
}
|
|
|
|
/* ---- SECTION DIVIDERS (full bleed) ---- */
|
|
.section-divider-left {
|
|
margin-left: -28px;
|
|
margin-right: -24px;
|
|
}
|
|
|
|
.section-divider-right {
|
|
margin-left: -24px;
|
|
margin-right: -28px;
|
|
}
|
|
|
|
/* ---- MULTI-COLUMN ROWS ---- */
|
|
.row-2 {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 14px;
|
|
}
|
|
|
|
/* ---- LABEL WITH PRIVACY ---- */
|
|
.label-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.label-row label {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* ---- AVATAR PICKER ---- */
|
|
.avatar-row {
|
|
display: flex;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
|
|
.avatar-option {
|
|
width: 40px;
|
|
height: 40px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px dashed var(--border);
|
|
background: var(--bg);
|
|
cursor: pointer;
|
|
padding: 3px;
|
|
transition: all 0.12s;
|
|
}
|
|
|
|
.avatar-option img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.avatar-option:hover {
|
|
border-color: var(--candle-dim);
|
|
background: var(--surface-hover);
|
|
}
|
|
|
|
.avatar-option.selected {
|
|
border: 2px solid var(--candle);
|
|
background: var(--surface);
|
|
}
|
|
|
|
/* ---- TEXTAREA RESIZE ---- */
|
|
.field textarea {
|
|
resize: vertical;
|
|
}
|
|
|
|
/* ---- CHAR COUNT ---- */
|
|
.char-count {
|
|
font-size: 10px;
|
|
color: var(--text-faint);
|
|
text-align: right;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
/* ---- TOGGLE FIELD ---- */
|
|
.toggle-field {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.toggle-label {
|
|
font-size: 12px;
|
|
color: var(--text);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.toggle-sub {
|
|
display: block;
|
|
font-size: 11px;
|
|
color: var(--text-faint);
|
|
margin-top: 1px;
|
|
}
|
|
|
|
/* ---- CHECKBOX GRID ---- */
|
|
.checkbox-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 3px 12px;
|
|
}
|
|
|
|
.checkbox-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-size: 11px;
|
|
color: var(--text-dim);
|
|
cursor: pointer;
|
|
padding: 2px 0;
|
|
user-select: none;
|
|
}
|
|
|
|
.checkbox-item .cb {
|
|
width: 13px;
|
|
height: 13px;
|
|
border: 1px dashed var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 9px;
|
|
color: transparent;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.checkbox-item.checked .cb {
|
|
border-color: var(--candle);
|
|
border-style: solid;
|
|
color: var(--candle);
|
|
}
|
|
|
|
.checkbox-item:hover {
|
|
color: var(--text);
|
|
}
|
|
|
|
/* ---- PEER SUPPORT PANEL ---- */
|
|
.peer-panel {
|
|
border: 1px dashed var(--border);
|
|
padding: 12px 14px;
|
|
margin-top: 4px;
|
|
margin-bottom: 12px;
|
|
background: var(--surface);
|
|
}
|
|
|
|
.peer-panel .suggested {
|
|
font-size: 10px;
|
|
color: var(--text-faint);
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.peer-panel .suggested a {
|
|
color: var(--candle);
|
|
text-decoration: underline;
|
|
cursor: pointer;
|
|
margin-left: 4px;
|
|
}
|
|
|
|
/* ---- DISABLED BUTTON ---- */
|
|
.btn:disabled {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* ---- SAVE BAR ---- */
|
|
.save-bar {
|
|
margin-left: -28px;
|
|
margin-right: -28px;
|
|
padding: 16px 28px 24px;
|
|
margin-top: 20px;
|
|
border-top: 1px dashed var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.save-msg {
|
|
font-size: 11px;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.save-msg-ok {
|
|
color: var(--green, var(--candle));
|
|
}
|
|
|
|
.save-msg-err {
|
|
color: var(--ember);
|
|
}
|
|
|
|
/* ---- RESPONSIVE ---- */
|
|
@media (max-width: 1024px) {
|
|
.profile-columns {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.profile-col-left {
|
|
padding-right: 0;
|
|
border-right: none;
|
|
border-bottom: 1px dashed var(--border);
|
|
padding-bottom: 20px;
|
|
margin-bottom: 20px;
|
|
margin-left: -28px;
|
|
margin-right: -28px;
|
|
padding-left: 28px;
|
|
padding-right: 28px;
|
|
}
|
|
|
|
.profile-col-right {
|
|
padding-left: 0;
|
|
}
|
|
|
|
.section-divider-left,
|
|
.section-divider-right {
|
|
margin-left: -28px;
|
|
margin-right: -28px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.page-content {
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.row-2 {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.checkbox-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.profile-col-left {
|
|
margin-left: -16px;
|
|
margin-right: -16px;
|
|
padding-left: 16px;
|
|
padding-right: 16px;
|
|
}
|
|
|
|
.section-divider-left,
|
|
.section-divider-right {
|
|
margin-left: -16px;
|
|
margin-right: -16px;
|
|
}
|
|
|
|
.save-bar {
|
|
margin-left: -16px;
|
|
margin-right: -16px;
|
|
padding-left: 16px;
|
|
padding-right: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
</style>
|