684 lines
24 KiB
Vue
Executable File
684 lines
24 KiB
Vue
Executable File
<template>
|
|
<div class="house-view">
|
|
<StatusBar />
|
|
<h2>{{ $t('falukant.house.title') }}</h2>
|
|
<section
|
|
v-if="debtorsPrison.active"
|
|
class="house-debt-warning surface-card"
|
|
:class="{ 'is-prison': debtorsPrison.inDebtorsPrison }"
|
|
>
|
|
<strong>
|
|
{{ debtorsPrison.inDebtorsPrison
|
|
? $t('falukant.bank.debtorsPrison.titlePrison')
|
|
: $t('falukant.bank.debtorsPrison.titleWarning') }}
|
|
</strong>
|
|
<p>
|
|
{{ debtorsPrison.inDebtorsPrison
|
|
? $t('falukant.house.debtorsPrison.houseRisk')
|
|
: $t('falukant.house.debtorsPrison.houseWarning') }}
|
|
</p>
|
|
</section>
|
|
<div class="existing-house">
|
|
<div :style="houseType ? houseStyle(houseType.position, 341) : {}" class="house"></div>
|
|
<div class="status-panel surface-card">
|
|
<h3>{{ $t('falukant.house.statusreport') }}</h3>
|
|
<div class="status-cards">
|
|
<article v-for="(value, key) in status" :key="key" class="status-card">
|
|
<div>
|
|
<span class="status-card__label">{{ $t(`falukant.house.status.${key}`) }}</span>
|
|
<strong>{{ conditionLabel(value) }}</strong>
|
|
</div>
|
|
<button v-if="value < 100" @click="renovate(key)">
|
|
{{ $t('falukant.house.renovate') }} ({{ getRenovationCost(key, value) }})
|
|
</button>
|
|
</article>
|
|
<article class="status-card status-card--summary">
|
|
<div>
|
|
<span class="status-card__label">{{ $t('falukant.house.worth') }}</span>
|
|
<strong>{{ getWorth() }} {{ currency }}</strong>
|
|
</div>
|
|
<div class="status-card__actions">
|
|
<button @click="renovateAll" :disabled="allRenovated">
|
|
{{ $t('falukant.house.renovateAll') }} ({{ getAllRenovationCost() }})
|
|
</button>
|
|
<button class="button-secondary" @click="sellHouse">
|
|
{{ $t('falukant.house.sell') }}
|
|
</button>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<section v-if="userHouse" class="servants-panel surface-card">
|
|
<div class="servants-panel__header">
|
|
<div>
|
|
<h3>{{ $t('falukant.house.servants.title') }}</h3>
|
|
<p>{{ $t('falukant.house.servants.description') }}</p>
|
|
</div>
|
|
<div class="servants-panel__actions">
|
|
<button @click="hireServant">
|
|
{{ $t('falukant.house.servants.actions.hire') }}
|
|
</button>
|
|
<button class="button-secondary" :disabled="(userHouse.servantCount || 0) <= 0" @click="dismissServant">
|
|
{{ $t('falukant.house.servants.actions.dismiss') }}
|
|
</button>
|
|
<button class="button-secondary" @click="tidyHousehold">
|
|
{{ $t('falukant.house.servants.actions.tidy') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="servants-grid">
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.house.servants.count') }}</span>
|
|
<strong>{{ userHouse.servantCount || 0 }}</strong>
|
|
</article>
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.house.servants.expectedRange') }}</span>
|
|
<strong>{{ servantSummary.expectedMin }} - {{ servantSummary.expectedMax }}</strong>
|
|
</article>
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.house.servants.monthlyCost') }}</span>
|
|
<strong>{{ formatPrice(servantSummary.monthlyCost || 0) }} {{ currency }}</strong>
|
|
</article>
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.house.servants.quality') }}</span>
|
|
<strong>{{ userHouse.servantQuality || 0 }}</strong>
|
|
</article>
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.house.servants.householdOrder') }}</span>
|
|
<strong>{{ userHouse.householdOrder || 0 }}</strong>
|
|
</article>
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.family.householdTension.score') }}</span>
|
|
<strong>{{ userHouse.householdTensionScore ?? 0 }}</strong>
|
|
</article>
|
|
<article class="servant-card">
|
|
<span class="servant-card__label">{{ $t('falukant.house.servants.staffingState.label') }}</span>
|
|
<strong>{{ $t(`falukant.house.servants.staffingState.${servantSummary.staffingState || 'fitting'}`) }}</strong>
|
|
</article>
|
|
</div>
|
|
|
|
<div class="servants-settings">
|
|
<label class="servants-settings__label">
|
|
{{ $t('falukant.house.servants.payLevel') }}
|
|
<select v-model="servantPayLevel" @change="updateServantPayLevel" class="servants-settings__select">
|
|
<option v-for="option in servantPayOptions" :key="option" :value="option">
|
|
{{ $t(`falukant.house.servants.payLevels.${option}`) }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<div class="servants-settings__state">
|
|
{{ $t('falukant.house.servants.orderState.label') }}:
|
|
<strong>{{ $t(`falukant.house.servants.orderState.${servantSummary.orderState || 'stable'}`) }}</strong>
|
|
</div>
|
|
</div>
|
|
<div v-if="Array.isArray(userHouse.householdTensionReasonsJson) && userHouse.householdTensionReasonsJson.length > 0" class="servants-reasons">
|
|
<span class="servants-reasons__label">{{ $t('falukant.family.householdTension.reasonsLabel') }}</span>
|
|
<div class="servants-reasons__list">
|
|
<span v-for="reason in userHouse.householdTensionReasonsJson" :key="reason" class="servants-reasons__badge">
|
|
{{ $t('falukant.family.householdTension.reasons.' + reason) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="buyable-houses">
|
|
<h3>{{ $t('falukant.house.buyablehouses') }}</h3>
|
|
<div class="houses-list">
|
|
<div v-for="house in buyableHouses" :key="house.id" class="house-item">
|
|
<div :style="house.houseType ? houseStyle(house.houseType.position, 114) : {}"
|
|
class="house-preview"></div>
|
|
<div class="house-info">
|
|
<h4>{{ $t(`falukant.house.type.${house.houseType.labelTr}`) }}</h4>
|
|
<div class="buyable-house-stats">
|
|
<div v-for="(val, prop) in house" :key="prop"
|
|
v-if="['roofCondition', 'wallCondition', 'floorCondition', 'windowCondition'].includes(prop)"
|
|
class="buyable-house-stat">
|
|
<span>{{ $t(`falukant.house.status.${prop}`) }}</span>
|
|
<strong>{{ conditionLabel(val) }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="buyable-house-price">
|
|
{{ $t('falukant.house.price') }}: {{ buyCost(house) }}
|
|
</div>
|
|
<button @click="buyHouse(house.id)" :disabled="debtorsPrison.inDebtorsPrison">
|
|
{{ $t('falukant.house.buy') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import StatusBar from '@/components/falukant/StatusBar.vue';
|
|
import apiClient from '@/utils/axios.js';
|
|
import { mapState } from 'vuex';
|
|
import { showError, showSuccess, confirmAction } from '@/utils/feedback.js';
|
|
|
|
export default {
|
|
name: 'HouseView',
|
|
components: { StatusBar },
|
|
data() {
|
|
return {
|
|
userHouse: null,
|
|
houseType: {},
|
|
status: {},
|
|
servantSummary: {
|
|
expectedMin: 0,
|
|
expectedMax: 0,
|
|
monthlyCost: 0,
|
|
staffingState: 'fitting',
|
|
orderState: 'stable'
|
|
},
|
|
servantPayLevel: 'normal',
|
|
servantPayOptions: ['low', 'normal', 'high'],
|
|
buyableHouses: [],
|
|
currency: '€',
|
|
debtorsPrison: {
|
|
active: false,
|
|
inDebtorsPrison: false
|
|
}
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState(['socket', 'daemonSocket', 'user']),
|
|
allRenovated() {
|
|
return Object.values(this.status).every(v => v >= 100);
|
|
}
|
|
},
|
|
methods: {
|
|
async loadData() {
|
|
try {
|
|
const userRes = await apiClient.get('/api/falukant/houses');
|
|
this.userHouse = userRes.data;
|
|
this.houseType = this.userHouse.houseType;
|
|
this.debtorsPrison = this.userHouse.debtorsPrison || {
|
|
active: false,
|
|
inDebtorsPrison: false
|
|
};
|
|
const { roofCondition, wallCondition, floorCondition, windowCondition } = this.userHouse;
|
|
this.status = { roofCondition, wallCondition, floorCondition, windowCondition };
|
|
this.servantSummary = this.userHouse.servantSummary || this.servantSummary;
|
|
this.servantPayLevel = this.userHouse.servantPayLevel || 'normal';
|
|
|
|
const buyRes = await apiClient.get('/api/falukant/houses/buyable');
|
|
this.buyableHouses = buyRes.data;
|
|
} catch (err) {
|
|
console.error('Error loading house data', err);
|
|
}
|
|
},
|
|
conditionLabel(value) {
|
|
const v = Number(value) || 0;
|
|
if (v >= 95) return this.$t('falukant.conditionBand.excellent');
|
|
if (v >= 72) return this.$t('falukant.conditionBand.veryGood');
|
|
if (v >= 54) return this.$t('falukant.conditionBand.good');
|
|
if (v >= 39) return this.$t('falukant.conditionBand.moderate');
|
|
if (v >= 22) return this.$t('falukant.conditionBand.bad');
|
|
if (v >= 6) return this.$t('falukant.conditionBand.veryBad');
|
|
if (v >= 1) return this.$t('falukant.conditionBand.catastrophic');
|
|
return this.$t('falukant.conditionBand.unknown');
|
|
},
|
|
houseStyle(position, picSize) {
|
|
const columns = 3;
|
|
const size = picSize;
|
|
const index = position - 1;
|
|
const x = (index % columns) * size;
|
|
const y = Math.floor(index / columns) * size;
|
|
return {
|
|
backgroundImage: 'url("/images/falukant/houses.png")',
|
|
backgroundPosition: `-${x}px -${y}px`,
|
|
backgroundSize: `${columns * size}px auto`
|
|
};
|
|
},
|
|
formatPrice(value) {
|
|
return new Intl.NumberFormat('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
|
},
|
|
getRenovationCost(key, value) {
|
|
if (!this.userHouse || !this.userHouse.houseType) return this.formatPrice(0);
|
|
const base = this.userHouse.houseType.cost || 0;
|
|
const weights = { roofCondition: 0.25, wallCondition: 0.25, floorCondition: 0.25, windowCondition: 0.25 };
|
|
const weight = weights[key] || 0;
|
|
const missing = 100 - value;
|
|
const cost = (missing / 100) * base * weight;
|
|
return this.formatPrice(cost);
|
|
},
|
|
getAllRenovationCost() {
|
|
const total = Object.keys(this.status).reduce((sum, k) => {
|
|
const raw = parseFloat(this.getRenovationCost(k, this.status[k]).replace(/\./g, '').replace(',', '.'));
|
|
return sum + (isNaN(raw) ? 0 : raw);
|
|
}, 0);
|
|
return this.formatPrice(total * 0.8);
|
|
},
|
|
getWorth() {
|
|
const vals = Object.values(this.status);
|
|
if (!vals.length || !this.houseType) return this.formatPrice(0);
|
|
const avg = vals.reduce((s, v) => s + v, 0) / vals.length;
|
|
const price = this.houseType.cost || 0;
|
|
return this.formatPrice(price * avg / 100 * 0.8);
|
|
},
|
|
buyCost(house) {
|
|
if (!house || !house.houseType) return this.formatPrice(0);
|
|
const avg = (house.roofCondition + house.wallCondition + house.floorCondition + house.windowCondition) / 4;
|
|
return this.formatPrice(house.houseType.cost * avg / 100);
|
|
},
|
|
async renovate(key) {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/renovate', { element: key });
|
|
await this.loadData();
|
|
} catch (err) {
|
|
console.error('Error renovating', err);
|
|
}
|
|
},
|
|
async renovateAll() {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/renovate-all');
|
|
await this.loadData();
|
|
} catch (err) {
|
|
console.error('Error renovating all', err);
|
|
}
|
|
},
|
|
async sellHouse() {
|
|
const confirmed = await confirmAction(this, {
|
|
title: this.$t('falukant.house.sell'),
|
|
text: this.$t('falukant.house.sellConfirm')
|
|
});
|
|
if (!confirmed) return;
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/sell');
|
|
await this.loadData();
|
|
showSuccess(this, this.$t('falukant.house.sellSuccess'));
|
|
} catch (err) {
|
|
console.error('Error selling house', err);
|
|
showError(this, this.$t('falukant.house.sellError'));
|
|
}
|
|
},
|
|
async buyHouse(id) {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses', { houseId: id });
|
|
await this.loadData();
|
|
showSuccess(this, this.$t('falukant.house.buySuccess'));
|
|
} catch (err) {
|
|
console.error('Error buying house', err);
|
|
showError(this, this.$t('falukant.house.buyError'));
|
|
}
|
|
},
|
|
async hireServant() {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/servants/hire', { amount: 1 });
|
|
await this.loadData();
|
|
showSuccess(this, this.$t('falukant.house.servants.actions.hireSuccess'));
|
|
} catch (err) {
|
|
console.error('Error hiring servant', err);
|
|
showError(this, this.$t('falukant.house.servants.actions.hireError'));
|
|
}
|
|
},
|
|
async dismissServant() {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/servants/dismiss', { amount: 1 });
|
|
await this.loadData();
|
|
showSuccess(this, this.$t('falukant.house.servants.actions.dismissSuccess'));
|
|
} catch (err) {
|
|
console.error('Error dismissing servant', err);
|
|
showError(this, this.$t('falukant.house.servants.actions.dismissError'));
|
|
}
|
|
},
|
|
async updateServantPayLevel() {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/servants/pay-level', {
|
|
payLevel: this.servantPayLevel
|
|
});
|
|
await this.loadData();
|
|
showSuccess(this, this.$t('falukant.house.servants.actions.payLevelSuccess'));
|
|
} catch (err) {
|
|
console.error('Error updating servant pay level', err);
|
|
showError(this, this.$t('falukant.house.servants.actions.payLevelError'));
|
|
}
|
|
},
|
|
async tidyHousehold() {
|
|
try {
|
|
await apiClient.post('/api/falukant/houses/order');
|
|
await this.loadData();
|
|
showSuccess(this, this.$t('falukant.house.servants.actions.tidySuccess'));
|
|
} catch (err) {
|
|
console.error('Error tidying household', err);
|
|
showError(this, this.$t('falukant.house.servants.actions.tidyError'));
|
|
}
|
|
},
|
|
handleDaemonMessage(evt) {
|
|
try {
|
|
const msg = JSON.parse(evt.data);
|
|
if (!this.matchesCurrentUser(msg)) return;
|
|
if (['houseupdated', 'falukantUpdateStatus', 'falukantUpdateDebt', 'falukantHouseUpdate'].includes(msg.event)) this.loadData();
|
|
} catch { }
|
|
},
|
|
matchesCurrentUser(eventData) {
|
|
if (eventData?.user_id == null) {
|
|
return true;
|
|
}
|
|
const currentIds = [this.user?.id, this.user?.hashedId]
|
|
.filter(Boolean)
|
|
.map((value) => String(value));
|
|
return currentIds.includes(String(eventData.user_id));
|
|
},
|
|
setupSocketEvents() {
|
|
if (this.socket) {
|
|
this.socket.on('falukantHouseUpdate', (data) => {
|
|
this.handleEvent({ event: 'falukantHouseUpdate', ...data });
|
|
});
|
|
this.socket.on('falukantUpdateDebt', (data) => {
|
|
this.handleEvent({ event: 'falukantUpdateDebt', ...data });
|
|
});
|
|
this.socket.on('falukantUpdateStatus', (data) => {
|
|
this.handleEvent({ event: 'falukantUpdateStatus', ...data });
|
|
});
|
|
} else {
|
|
setTimeout(() => this.setupSocketEvents(), 1000);
|
|
}
|
|
},
|
|
handleEvent(eventData) {
|
|
switch (eventData.event) {
|
|
case 'falukantUpdateStatus':
|
|
case 'falukantUpdateDebt':
|
|
case 'falukantHouseUpdate':
|
|
this.loadData();
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
async mounted() {
|
|
await this.loadData();
|
|
this.setupSocketEvents();
|
|
if (this.daemonSocket) {
|
|
this.daemonSocket.addEventListener('message', this.handleDaemonMessage);
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
if (this.daemonSocket) {
|
|
this.daemonSocket.removeEventListener('message', this.handleDaemonMessage);
|
|
}
|
|
if (this.socket) {
|
|
this.socket.off('falukantHouseUpdate', this.loadData);
|
|
this.socket.off('falukantUpdateDebt', this.loadData);
|
|
this.socket.off('falukantUpdateStatus', this.loadData);
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.house-view {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 20px;
|
|
/* AppContent gibt dem letzten Kind flex:1 + min-height:0 — sonst schrumpfen
|
|
Spalten-Kinder und überlagern sich (z. B. „Dienerschaft“ unter „Kaufe ein Haus“). */
|
|
flex: 0 0 auto;
|
|
min-height: min-content;
|
|
width: 100%;
|
|
}
|
|
|
|
.existing-house,
|
|
.servants-panel,
|
|
.buyable-houses {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
h2 {
|
|
padding-top: 20px;
|
|
margin: 0 0 10px;
|
|
}
|
|
|
|
.house-debt-warning {
|
|
padding: 16px 18px;
|
|
border: 1px solid rgba(180, 120, 40, 0.32);
|
|
background: linear-gradient(180deg, rgba(255, 244, 223, 0.95), rgba(255, 250, 239, 0.98));
|
|
}
|
|
|
|
.house-debt-warning.is-prison {
|
|
border-color: rgba(146, 57, 40, 0.4);
|
|
background: linear-gradient(180deg, rgba(255, 232, 225, 0.96), rgba(255, 245, 241, 0.98));
|
|
}
|
|
|
|
.house-debt-warning p {
|
|
margin: 6px 0 0;
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.existing-house {
|
|
display: flex;
|
|
gap: 20px;
|
|
}
|
|
|
|
.house {
|
|
width: 341px;
|
|
height: 341px;
|
|
background-repeat: no-repeat;
|
|
image-rendering: crisp-edges;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.status-panel {
|
|
flex: 1;
|
|
padding: 18px;
|
|
}
|
|
|
|
.servants-panel {
|
|
padding: 18px;
|
|
}
|
|
|
|
.servants-panel__header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
align-items: flex-start;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.servants-panel__header h3 {
|
|
margin: 0 0 6px;
|
|
}
|
|
|
|
.servants-panel__header p {
|
|
margin: 0;
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.servants-panel__actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.servants-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 12px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.servant-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
padding: 14px 16px;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
background: rgba(255, 255, 255, 0.68);
|
|
}
|
|
|
|
.servant-card__label {
|
|
color: var(--color-text-secondary);
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.servants-settings {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.servants-settings__label {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.servants-settings__select {
|
|
min-width: 200px;
|
|
}
|
|
|
|
.servants-settings__state {
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.servants-reasons {
|
|
display: grid;
|
|
gap: 8px;
|
|
margin-top: 16px;
|
|
}
|
|
|
|
.servants-reasons__label {
|
|
color: var(--color-text-secondary);
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.servants-reasons__list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.servants-reasons__badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 4px 10px;
|
|
border-radius: 999px;
|
|
background: rgba(188, 84, 61, 0.12);
|
|
color: #9a3c26;
|
|
font-size: 0.8rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.buyable-houses {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.houses-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
/* vertical list */
|
|
gap: 20px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
/* vertical scroll if needed */
|
|
}
|
|
|
|
.house-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
align-items: center;
|
|
padding: 18px;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
background: rgba(255, 255, 255, 0.68);
|
|
}
|
|
|
|
.house-preview {
|
|
width: 100px;
|
|
height: 100px;
|
|
background-repeat: no-repeat;
|
|
image-rendering: crisp-edges;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 4px;
|
|
background-size: contain;
|
|
/* scale image to container */
|
|
background-position: center;
|
|
/* center sprite */
|
|
}
|
|
|
|
.house-info {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.status-cards,
|
|
.buyable-house-stats {
|
|
display: grid;
|
|
gap: 12px;
|
|
}
|
|
|
|
.status-card,
|
|
.buyable-house-stat {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
padding: 14px 16px;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
background: rgba(255, 255, 255, 0.68);
|
|
}
|
|
|
|
.status-card__label,
|
|
.buyable-house-stat span {
|
|
display: block;
|
|
margin-bottom: 4px;
|
|
color: var(--color-text-secondary);
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.status-card--summary {
|
|
align-items: flex-start;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.status-card__actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.buyable-house-price {
|
|
font-weight: 700;
|
|
}
|
|
|
|
button {
|
|
padding: 6px 12px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
@media (max-width: 960px) {
|
|
.existing-house {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.servants-panel__header {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.house {
|
|
width: min(341px, 100%);
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.status-card,
|
|
.buyable-house-stat {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
}
|
|
</style>
|