@@ -3,7 +3,7 @@ import { ref, computed, onMounted } from 'vue';
import { useRoute , useRouter } from 'vue-router' ;
import { useRoute , useRouter } from 'vue-router' ;
import { useI18n } from 'vue-i18n' ;
import { useI18n } from 'vue-i18n' ;
import api from '../api' ;
import api from '../api' ;
import { formatMoney } from '../utils/localeDisplay' ;
import { formatMoney , parseAmount } from '../utils/localeDisplay' ;
import GoldSpinner from '../components/GoldSpinner.vue' ;
import GoldSpinner from '../components/GoldSpinner.vue' ;
import { usePullToRefresh } from '../composables/usePullToRefresh' ;
import { usePullToRefresh } from '../composables/usePullToRefresh' ;
import StatusWatermark from '../components/StatusWatermark.vue' ;
import StatusWatermark from '../components/StatusWatermark.vue' ;
@@ -52,6 +52,8 @@ const statusKey = computed(() => {
const statusLabel = computed ( ( ) => t ( ` history.status_ ${ statusKey . value } ` ) ) ;
const statusLabel = computed ( ( ) => t ( ` history.status_ ${ statusKey . value } ` ) ) ;
const isSettled = computed ( ( ) => statusKey . value !== 'pending' ) ;
const placedDateTime = computed ( ( ) => {
const placedDateTime = computed ( ( ) => {
if ( ! bet . value ) return '' ;
if ( ! bet . value ) return '' ;
const d = new Date ( bet . value . placedAt ) ;
const d = new Date ( bet . value . placedAt ) ;
@@ -64,10 +66,53 @@ const returnAmount = computed(() => {
if ( ! bet . value ) return '' ;
if ( ! bet . value ) return '' ;
if ( statusKey . value === 'won' ) return formatMoney ( bet . value . actualReturn , locale . value ) ;
if ( statusKey . value === 'won' ) return formatMoney ( bet . value . actualReturn , locale . value ) ;
if ( statusKey . value === 'pending' ) return formatMoney ( bet . value . potentialReturn , locale . value ) ;
if ( statusKey . value === 'pending' ) return formatMoney ( bet . value . potentialReturn , locale . value ) ;
if ( statusKey . value === 'lost' ) return formatMoney ( - parseFloat ( String ( bet . value . stake ? ? 0 ) ) , locale . value ) ;
if ( statusKey . value === 'lost' ) return formatMoney ( - parseAmount ( bet . value . stake ) , locale . value ) ;
return formatMoney ( bet . value . actualReturn ? ? bet . value . potentialReturn , locale . value ) ;
return formatMoney ( bet . value . actualReturn ? ? bet . value . potentialReturn , locale . value ) ;
} ) ;
} ) ;
const profitAmount = computed ( ( ) => {
if ( ! bet . value || statusKey . value !== 'won' ) return '' ;
const profit = parseAmount ( bet . value . actualReturn ) - parseAmount ( bet . value . stake ) ;
return ` + ${ formatMoney ( profit , locale . value ) } ` ;
} ) ;
const heroAmount = computed ( ( ) => {
if ( statusKey . value === 'won' ) return profitAmount . value ;
return returnAmount . value ;
} ) ;
const heroTitle = computed ( ( ) => {
if ( statusKey . value === 'won' ) return t ( 'history.profit' ) ;
if ( statusKey . value === 'pending' ) return t ( 'history.est_return' ) ;
return statusLabel . value ;
} ) ;
const formulaText = computed ( ( ) => {
if ( ! bet . value || ! oddsText . value || statusKey . value === 'pending' ) return '' ;
return t ( 'history.stake_to_return' , {
stake : stakeAmount . value ,
odds : oddsText . value ,
amount : returnAmount . value ,
} ) ;
} ) ;
const stakeAmount = computed ( ( ) =>
bet . value ? formatMoney ( bet . value . stake , locale . value ) : '' ,
) ;
const oddsText = computed ( ( ) => {
const o = bet . value ? . totalOdds ;
if ( o == null || o === '' || o === 0 ) return '' ;
const n = parseFloat ( String ( o ) ) ;
return Number . isFinite ( n ) ? n . toFixed ( 2 ) : String ( o ) ;
} ) ;
const betTypeLabel = computed ( ( ) => {
if ( ! bet . value ) return '' ;
if ( bet . value . isParlay ) return t ( 'bet.parlay' ) ;
return bet . value . betType || '' ;
} ) ;
function formatOdds ( v : unknown ) : string {
function formatOdds ( v : unknown ) : string {
const n = parseFloat ( String ( v ) ) ;
const n = parseFloat ( String ( v ) ) ;
return isNaN ( n ) || n <= 0 ? '-' : n . toFixed ( 2 ) ;
return isNaN ( n ) || n <= 0 ? '-' : n . toFixed ( 2 ) ;
@@ -116,13 +161,20 @@ const matchTitle = computed(() => {
return bet . value . matchTitle ;
return bet . value . matchTitle ;
} ) ;
} ) ;
const myPick = computed ( ( ) => {
const pickMarket = computed ( ( ) => {
if ( ! bet . value || bet . value . isParlay ) return '' ;
if ( ! bet . value || bet . value . isParlay ) return '' ;
const raw = bet . value . pickLabel ? ? '' ;
const raw = bet . value . pickLabel ? ? '' ;
if ( locale . value === 'zh-CN' || ! raw ) return raw ;
const ci = raw . indexOf ( ': ' ) ;
const ci = raw . indexOf ( ': ' ) ;
if ( ci < 0 ) return raw ;
return ci >= 0 ? raw . slice ( 0 , ci ) : raw ;
return raw . slice ( 0 , ci + 2 ) + translateSel ( raw . slice ( ci + 2 ) ) ;
} ) ;
const pickSelection = computed ( ( ) => {
if ( ! bet . value || bet . value . isParlay ) return '' ;
const raw = bet . value . pickLabel ? ? '' ;
const ci = raw . indexOf ( ': ' ) ;
if ( ci < 0 ) return '' ;
const sel = raw . slice ( ci + 2 ) ;
return locale . value === 'zh-CN' ? sel : translateSel ( sel ) ;
} ) ;
} ) ;
const matchPhase = computed (
const matchPhase = computed (
@@ -130,20 +182,20 @@ const matchPhase = computed(
) ;
) ;
const matchPhaseText = computed ( ( ) => matchPhaseLabel ( t , matchPhase . value ) ) ;
const matchPhaseText = computed ( ( ) => matchPhaseLabel ( t , matchPhase . value ) ) ;
const showPhaseWatermark = computed (
( ) => ! isSettled . value && matchPhase . value && matchPhase . value !== 'open' ,
) ;
< / script >
< / script >
< template >
< template >
< div class = "detail-page" >
< div class = "detail-page" >
< div
< div class = "pull-indicator" :style = "pullIndicatorStyle()" >
class = "pull-indicator"
:style = "pullIndicatorStyle()"
>
< GoldSpinner v-if = "spinning" :size="28" :progress="progress" :active="spinning" / >
< GoldSpinner v-if = "spinning" :size="28" :progress="progress" :active="spinning" / >
< / div >
< / div >
<!-- back bar -- >
< div class = "top-bar" >
< div class = "top-bar" >
< button class = "back-btn" @click ="router.back()" > ‹ {{ t ( ' history.back ' ) }} < / button >
< button type = "button" class= "back-btn" @click ="router.back()" > ‹ {{ t ( ' history.back ' ) }} < / button >
< / div >
< / div >
< div v-if = "loading" class="state" >
< div v-if = "loading" class="state" >
@@ -151,113 +203,131 @@ const matchPhaseText = computed(() => matchPhaseLabel(t, matchPhase.value));
< / div >
< / div >
< div v-else-if = "notFound || !bet" class="state" > {{ t ( ' history.not_found ' ) }} < / div >
< div v-else-if = "notFound || !bet" class="state" > {{ t ( ' history.not_found ' ) }} < / div >
< templat e v-else >
< articl e v-else class = "receipt" >
< ! - - status hero - - >
<!-- result strip -- >
< div class = "hero " :class = "statusKey" >
< div class = "receipt-result " :class = "statusKey" >
< span class = "hero-status" > { { statusLabel } } < / span >
< div class = "result-icon" :class = "statusKey" >
< span class = "hero-return" > { { returnAmount } } < / span >
< svg v-if = "statusKey === 'won'" viewBox="0 0 24 24" aria-hidden="true" >
< span class = "hero-return-label" >
< circle cx = "12" cy = "12" r = "11" fill = "none" stroke = "currentColor" stroke -width = " 1.5 " / >
{ { statusKey === 'pending' ? t ( 'history.est_return' ) : t ( 'history.return' ) } }
< path d = "M7 12.5l3 3 7-7" fill = "none" stroke = "currentColor" stroke -width = " 2 " stroke -linecap = " round " stroke -linejoin = " round " / >
< / span >
< / svg >
< svg v-else-if = "statusKey === 'lost'" viewBox="0 0 24 24" aria-hidden="true" >
< circle cx = "12" cy = "12" r = "11" fill = "none" stroke = "currentColor" stroke -width = " 1.5 " / >
< path d = "M8 8l8 8M16 8l-8 8" fill = "none" stroke = "currentColor" stroke -width = " 2 " stroke -linecap = " round " / >
< / svg >
< svg v-else-if = "statusKey === 'pending'" viewBox="0 0 24 24" aria-hidden="true" >
< circle cx = "12" cy = "12" r = "11" fill = "none" stroke = "currentColor" stroke -width = " 1.5 " / >
< path d = "M12 7v5l3 2" fill = "none" stroke = "currentColor" stroke -width = " 2 " stroke -linecap = " round " / >
< / svg >
< svg v-else viewBox = "0 0 24 24" aria -hidden = " true " >
< circle cx = "12" cy = "12" r = "11" fill = "none" stroke = "currentColor" stroke -width = " 1.5 " / >
< path d = "M8 12h8" fill = "none" stroke = "currentColor" stroke -width = " 2 " stroke -linecap = " round " / >
< / svg >
< / div >
< div class = "result-text" >
< span class = "result-label" > { { heroTitle } } < / span >
< span class = "result-amount" > { { heroAmount } } < / span >
< span v-if = "statusKey === 'won'" class="result-sub" >
{{ t ( ' history.return_incl_stake ' , { amount : returnAmount } ) }}
< / span >
< span v-else-if = "formulaText && statusKey !== 'pending'" class="result-sub" > {{ formulaText }} < / span >
< span v-else-if = "statusKey === 'pending' && oddsText" class="result-sub" >
{{ stakeAmount }} × {{ oddsText }}
< / span >
< / div >
< / div >
< / div >
<!-- match / parlay title -- >
< div class = "receipt-perforation" aria -hidden = " true " / >
< section class = "section" >
< div class = "section-head" >
< span class = "league-tag" >
{ { bet . isParlay ? t ( 'history.parlay_league' ) : ( bet . leagueName || t ( 'history.league_default' ) ) } }
< / span >
< span class = "placed-time" > { { placedDateTime } } < / span >
< / div >
< div class = "match-name" > { { matchTitle } } < / div >
<!-- single bet : score comparison -- >
<!-- body -- >
< div v-if = "!bet.isParlay" class="score-block" :class="{ 'score-block--phase': matchPhase && matchPhase !== 'open' }" >
< div class = "receipt-body" >
< div class = "receipt-meta" >
< div class = "meta-tags" >
< span v-if = "betTypeLabel" class="meta-tag" > {{ betTypeLabel }} < / span >
< span v-if = "oddsText" class="meta-tag dim" > @ {{ oddsText }} < / span >
< span v-if = "bet.isCashbacked" class="meta-tag cashback" > {{ t ( ' history.cashbacked ' ) }} < / span >
< / div >
< span class = "meta-time" > { { placedDateTime } } < / span >
< / div >
< h2 class = "match-title" > { { matchTitle } } < / h2 >
<!-- single pick -- >
< div v-if = "!bet.isParlay" class="pick-block" >
< StatusWatermark
< StatusWatermark
v-if = "matchPhase && matchPhase !== 'open' "
v-if = "showPhaseWatermark "
:label = "matchPhaseText"
:label = "matchPhaseText"
:variant = "matchPhaseVariant(matchPhase)"
:variant = "matchPhaseVariant(matchPhase! )"
size = "md"
size = "md"
/ >
/ >
<!-- my pick row -- >
< span class = "pick-market" > { { pickMarket } } < / span >
< div class = "row-label-val " >
< div class = "pick-line " >
< span class = "row-label " > { { t ( 'history.my_pick' ) } } < / span >
< span class = "pick-selection " > { { pickSelection } } < / span >
< span class = "row-val pick" > { { myPick } } < / span >
< span v-if = "oddsText" class="pick-odds" > @ {{ oddsText }} < / span >
< / div >
< / div >
<!-- scores -- >
< div v-if = "bet.matchScore?.ft || bet.matchScore?.ht" class="score-row" >
< div v-if = "bet.matchScore? .ft || bet.matchScore?.ht " class="score-chips " >
< span v-if = "bet.matchScore.ft" class="score-pill " >
< div v-if = "bet.matchScore.ft" class="score-item" >
< em > { { t ( 'history.ft' ) } } < / em > { { bet . matchScore . ft } }
< span class = "score-period" > { { t ( 'history.ft' ) } } < / span >
< / span >
< span class = "score-value" > { { bet . matchScore . ft } } < / span >
< span v-if = "bet.matchScore.ht" class="score-pill dim" >
< / div >
< em > { { t ( 'history.ht' ) } } < / em > { { bet . matchScore . ht } }
< div v-if = "bet.matchScore.ht" class="score-item" >
< span class = "score-period" > { { t ( 'history.ht' ) } } < / span >
< span class = "score-value muted" > { { bet . matchScore . ht } } < / span >
< / div >
< / div >
< div v-else-if = "statusKey === 'pending'" class="no-score" > {{ t ( ' history.awaiting_result ' ) }} < / div >
< / div >
< / section >
< ! - - parlay legs - - >
< section v-if = "bet.isParlay && bet.legs?.length" class="section" >
< div class = "section-title" > { { t ( 'history.legs' ) } } < / div >
< div class = "legs-list" >
< div v-for = "(leg, i) in bet.legs" :key="i" class="leg-row" :class="legStatusKey(leg.resultStatus)" >
< div class = "leg-left" >
< span class = "leg-num" :class = "legStatusKey(leg.resultStatus)" > { { i + 1 } } < / span >
< div class = "leg-body" >
< span class = "leg-match" > { { leg . matchTitle } } < / span >
< span class = "leg-pick-line" >
{ { leg . marketLabel } } : { { translateSel ( leg . selectionName ) } }
< / span >
< div v-if = "leg.score?.ft || leg.score?.ht" class="leg-scores" >
< span v-if = "leg.score.ft" > {{ t ( ' history.ft ' ) }} {{ leg.score.ft }} < / span >
< span v-if = "leg.score.ht" class="muted-score" > {{ t ( ' history.ht ' ) }} {{ leg.score.ht }} < / span >
< / div >
< / div >
< / div >
< div class = "leg-right" >
< span class = "leg-odds-val" > { { formatOdds ( leg . odds ) } } < / span >
< span v-if = "leg.resultStatus" class="leg-result-badge" :class="legStatusKey(leg.resultStatus)" >
{{ t ( ` history.status_ $ { legStatusKey ( leg.resultStatus ) } ` ) }}
< / span >
< / div >
< / div >
< / div >
< / section >
< ! - - bet summary - - >
< section class = "section" >
< div class = "section-title" > { { t ( 'history.summary' ) } } < / div >
< div class = "summary-rows" >
< div class = "sum-row" >
< span > { { t ( 'history.stake' ) } } < / span >
< span > { { formatMoney ( bet . stake , locale ) } } < / span >
< / div >
< div v-if = "bet.totalOdds" class="sum-row" >
< span > { { t ( 'history.odds' ) } } < / span >
< span class = "odds-val" > { { formatOdds ( bet . totalOdds ) } } < / span >
< / div >
< div class = "sum-row" >
< span > { { statusKey === 'pending' ? t ( 'history.est_return' ) : t ( 'history.return' ) } } < / span >
< span : class = "{ 'amt-won': statusKey === 'won', 'amt-pending': statusKey === 'pending', 'amt-lost': statusKey === 'lost' }" >
{ { returnAmount } }
< / span >
< / span >
< / div >
< / div >
< div class = "sum-row muted" >
< p v-else-if = "statusKey === 'pending'" class="awaiting" > {{ t ( ' history.awaiting_result ' ) }} < / p >
< span > { { t ( 'history.bet_no' ) } } < / span >
< / div >
< span class = "bet-no-val" > { { bet . betNo } } < / span >
< ! - - parlay legs - - >
< div v-if = "bet.isParlay && bet.legs?.length" class="legs-list" >
< div
v-for = "(leg, i) in bet.legs"
:key = "i"
class = "leg-item"
:class = "legStatusKey(leg.resultStatus)"
>
< div class = "leg-status-dot" :class = "legStatusKey(leg.resultStatus)" >
< svg v-if = "legStatusKey(leg.resultStatus) === 'won'" viewBox="0 0 12 12"><path d="M2 6l3 3 5-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" / > < / svg >
< svg v-else-if = "legStatusKey(leg.resultStatus) === 'lost'" viewBox="0 0 12 12"><path d="M3 3l6 6M9 3l-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" / > < / svg >
< span v-else > {{ i + 1 }} < / span >
< / div >
< div class = "leg-content" >
< div class = "leg-head" >
< span class = "leg-match" > { { leg . matchTitle } } < / span >
< span class = "leg-odds" > @ { { formatOdds ( leg . odds ) } } < / span >
< / div >
< span class = "leg-pick" > { { leg . marketLabel } } · { { translateSel ( leg . selectionName ) } } < / span >
< div v-if = "leg.score?.ft || leg.score?.ht" class="score-row compact" >
< span v-if = "leg.score.ft" class="score-pill sm" > < em > { { t ( 'history.ft' ) } } < / em > { { leg . score . ft } } < / span >
< span v-if = "leg.score.ht" class="score-pill sm dim" > < em > { { t ( 'history.ht' ) } } < / em > { { leg . score . ht } } < / span >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< / section >
< / div >
< / template >
< div class = "receipt-perforation" aria -hidden = " true " / >
<!-- footer -- >
< div class = "receipt-foot" >
< div class = "foot-row" >
< span > { { t ( 'history.stake' ) } } < / span >
< span > { { stakeAmount } } < / span >
< / div >
< div v-if = "bet.totalOdds" class="foot-row" >
< span > { { t ( 'history.odds' ) } } < / span >
< span class = "gold" > { { formatOdds ( bet . totalOdds ) } } < / span >
< / div >
< div class = "foot-row id" >
< span > { { t ( 'history.bet_no' ) } } < / span >
< span class = "mono" > { { bet . betNo } } < / span >
< / div >
< / div >
< / article >
< / div >
< / div >
< / template >
< / template >
< style scoped >
< style scoped >
. detail - page {
. detail - page {
padding - bottom : 32 px ;
padding - bottom : 36 px ;
}
}
. pull - indicator {
. pull - indicator {
@@ -269,360 +339,396 @@ const matchPhaseText = computed(() => matchPhaseLabel(t, matchPhase.value));
}
}
. top - bar {
. top - bar {
margin - bottom : 4 px ;
margin - bottom : 10 px ;
}
}
. back - btn {
. back - btn {
background : none ;
background : none ;
border : none ;
border : none ;
color : var ( -- primary - light , # d4af37 );
color : var ( -- primary - light ) ;
font - size : 15 px ;
font - size : 15 px ;
font - weight : 700 ;
font - weight : 700 ;
padding : 4 px 0 8 px ;
padding : 4 px 0 8 px ;
cursor : pointer ;
cursor : pointer ;
display : flex ;
align - items : center ;
gap : 2 px ;
}
}
. state {
. state {
text - align : center ;
text - align : center ;
padding : 60 px 20 px ;
padding : 60 px 20 px ;
color : # 666 ;
color : var ( -- text - muted ) ;
font - size : 14 px ;
font - size : 14 px ;
font - weight : 600 ;
font - weight : 600 ;
}
}
/* ── hero ── */
/* ── receipt ticket ── */
. hero {
. receipt {
border - radius : 14 px ;
position : relative ;
padding : 20 px 20 px 18 px ;
border - radius : var ( -- radius ) ;
background : rgba ( 18 , 18 , 18 , 0.96 ) ;
border : 1 px solid var ( -- border ) ;
box - shadow : var ( -- shadow ) ;
overflow : hidden ;
}
/* result strip */
. receipt - result {
display : flex ;
align - items : center ;
gap : 16 px ;
padding : 20 px 18 px 18 px ;
}
. receipt - result . won {
background : linear - gradient ( 135 deg , rgba ( 52 , 199 , 89 , 0.1 ) 0 % , transparent 70 % ) ;
}
. receipt - result . lost {
background : linear - gradient ( 135 deg , rgba ( 255 , 69 , 58 , 0.08 ) 0 % , transparent 70 % ) ;
}
. receipt - result . pending {
background : linear - gradient ( 135 deg , rgba ( 212 , 175 , 55 , 0.08 ) 0 % , transparent 70 % ) ;
}
. result - icon {
flex - shrink : 0 ;
width : 48 px ;
height : 48 px ;
}
. result - icon svg {
width : 100 % ;
height : 100 % ;
}
. result - icon . won { color : # 34 c759 ; }
. result - icon . lost { color : var ( -- danger ) ; }
. result - icon . pending { color : var ( -- primary - light ) ; }
. result - icon . push { color : var ( -- text - muted ) ; }
. result - text {
flex : 1 ;
min - width : 0 ;
display : flex ;
display : flex ;
flex - direction : column ;
flex - direction : column ;
align - items : center ;
gap : 2 px ;
gap : 4 px ;
margin - bottom : 12 px ;
text - align : center ;
}
}
. hero . won { background : linear - gradient ( 135 deg , rgba ( 61 , 184 , 101 , 0.15 ) , rgba ( 61 , 184 , 101 , 0.06 ) ) ; border : 1 px solid rgba ( 61 , 184 , 101 , 0.25 ) ; }
. result - label {
. hero . lost { background : linear - gradient ( 135 deg , rgba ( 224 , 80 , 80 , 0.12 ) , rgba ( 224 , 80 , 80 , 0.05 ) ) ; border : 1 px solid rgba ( 224 , 80 , 80 , 0.2 ) ; }
font - size : 12 px ;
. hero . pending { background : linear - gradient ( 135 deg , rgba ( 232 , 200 , 74 , 0.1 ) , rgba ( 232 , 200 , 74 , 0.04 ) ) ; border : 1 px solid rgba ( 232 , 200 , 74 , 0.2 ) ; }
font - weight : 700 ;
. hero . push { background : # 181818 ; border : 1 px solid # 2 a2a2a ; }
color : var ( -- text - muted ) ;
letter - spacing : 0.04 em ;
. hero - status {
font - size : 11 px ;
font - weight : 800 ;
letter - spacing : 0.1 em ;
text - transform : uppercase ;
text - transform : uppercase ;
opacity : 0.7 ;
}
}
. hero . won . hero - status { color : # 3 db865 ; }
. hero . lost . hero - status { color : # e05050 ; }
. hero . pending . hero - status { color : # e8c84a ; }
. hero . push . hero - status { color : # 888 ; }
. hero - return {
. result - amount {
font - size : 36 px ;
font - size : 32 px ;
font - weight : 900 ;
font - weight : 900 ;
letter - spacing : - 0.01 em ;
letter - spacing : - 0.02 em ;
line - height : 1.1 ;
line - height : 1.1 ;
}
}
. hero . won . hero - return { color : # 3 db865 ; text - shadow : 0 0 24 px rgba ( 61 , 184 , 101 , 0.3 ) ; }
. hero . lost . hero - return { color : # e05050 ; }
. hero . pending . hero - return { color : # e8c84a ; }
. hero . push . hero - return { color : # 777 ; }
. hero - return - label {
. receipt - result . won . result - amount { color : # 34 c759 ; }
font - size : 10.5 px ;
. receipt - result . lost . result - amount { color : var ( -- danger ) ; }
color : # 555 ;
. receipt - result . pending . result - amount { color : var ( -- primary - light ) ; }
. receipt - result . push . result - amount { color : var ( -- text - muted ) ; }
. result - sub {
font - size : 12 px ;
font - weight : 600 ;
font - weight : 600 ;
letter - spacing : 0.04 em ;
color : var ( -- text - muted ) ;
margin - top : 2 px ;
}
}
/* ── sections ── */
/* perforated divider */
. s ection {
. r eceipt - perfora tion {
background : # 141414 ;
height : 1 px ;
border : 1 px solid # 222 ;
margin : 0 14 px ;
border - radius : 12 px ;
background : repeating - linear - gradient (
padding : 14 px 16 px ;
90 deg ,
transparent 0 ,
transparent 4 px ,
rgba ( 255 , 255 , 255 , 0.08 ) 4 px ,
rgba ( 255 , 255 , 255 , 0.08 ) 8 px
) ;
position : relative ;
}
. receipt - perforation : : before ,
. receipt - perforation : : after {
content : '' ;
position : absolute ;
top : 50 % ;
width : 12 px ;
height : 12 px ;
border - radius : 50 % ;
background : var ( -- bg - body , # 000 ) ;
transform : translateY ( - 50 % ) ;
}
. receipt - perforation : : before { left : - 20 px ; }
. receipt - perforation : : after { right : - 20 px ; }
/* body */
. receipt - body {
padding : 16 px 18 px ;
}
. receipt - meta {
display : flex ;
justify - content : space - between ;
align - items : flex - start ;
gap : 10 px ;
margin - bottom : 10 px ;
margin - bottom : 10 px ;
}
}
. section - head {
. meta - tags {
display : flex ;
display : flex ;
justify - content : space - between ;
flex - wrap : wrap ;
align - items : center ;
gap : 6 px ;
}
. meta - tag {
font - size : 10 px ;
font - weight : 800 ;
letter - spacing : 0.05 em ;
text - transform : uppercase ;
padding : 3 px 8 px ;
border - radius : 4 px ;
color : var ( -- primary - light ) ;
background : rgba ( 212 , 175 , 55 , 0.1 ) ;
border : 1 px solid var ( -- border - gold - soft ) ;
}
. meta - tag . dim {
color : var ( -- text - muted ) ;
background : rgba ( 255 , 255 , 255 , 0.04 ) ;
border - color : var ( -- border ) ;
}
. meta - tag . cashback {
color : # f0b90b ;
background : rgba ( 240 , 185 , 11 , 0.1 ) ;
border - color : rgba ( 240 , 185 , 11 , 0.25 ) ;
}
. meta - time {
flex - shrink : 0 ;
font - size : 11 px ;
color : # 555 ;
font - weight : 600 ;
}
. match - title {
font - size : 16 px ;
font - weight : 800 ;
color : var ( -- text ) ;
line - height : 1.35 ;
margin - bottom : 14 px ;
}
/* pick block */
. pick - block {
position : relative ;
overflow : hidden ;
padding : 14 px ;
background : rgba ( 255 , 255 , 255 , 0.02 ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.06 ) ;
border - radius : var ( -- radius - sm ) ;
}
. pick - market {
display : block ;
font - size : 11 px ;
font - weight : 700 ;
color : var ( -- text - muted ) ;
letter - spacing : 0.04 em ;
margin - bottom : 6 px ;
margin - bottom : 6 px ;
}
}
. league - tag {
. pick - line {
font - size : 11 px ;
color : # 888 ;
font - weight : 700 ;
}
. placed - time {
font - size : 11 px ;
color : # 555 ;
font - weight : 600 ;
}
. match - name {
font - size : 17 px ;
font - weight : 900 ;
color : # f0f0f0 ;
line - height : 1.3 ;
margin - bottom : 10 px ;
}
/* score block */
. score - block {
position : relative ;
overflow : hidden ;
display : flex ;
flex - direction : column ;
gap : 8 px ;
}
. score - block -- phase {
padding : 8 px 0 4 px ;
}
. row - label - val {
display : flex ;
display : flex ;
justify - content : space - between ;
justify - content : space - between ;
align - items : center ;
align - items : center ;
gap : 8 px ;
gap : 10 px ;
}
}
. row - label {
. pick - selection {
font - size : 11 px ;
color : # 666 ;
font - weight : 600 ;
}
. row - val {
font - size : 13 px ;
font - weight : 700 ;
color : # c8c8c8 ;
}
. row - val . pick {
color : # d4af37 ;
}
. score - chips {
display : flex ;
gap : 8 px ;
}
. score - item {
display : flex ;
align - items : center ;
gap : 5 px ;
background : # 1 e1e1e ;
border : 1 px solid # 2 a2a2a ;
border - radius : 7 px ;
padding : 5 px 10 px ;
}
. score - period {
font - size : 10 px ;
color : # 666 ;
font - weight : 700 ;
letter - spacing : 0.05 em ;
text - transform : uppercase ;
}
. score - value {
font - size : 16 px ;
font - size : 16 px ;
font - weight : 900 ;
color : # e8e8e8 ;
}
. score - value . muted {
color : # 666 ;
font - size : 13 px ;
}
. no - score {
font - size : 11.5 px ;
color : # 555 ;
font - style : italic ;
}
/* section title */
. section - title {
font - size : 10.5 px ;
color : # 555 ;
font - weight : 800 ;
font - weight : 800 ;
letter - spacing : 0.08 em ;
color : var ( -- primary - light ) ;
}
. pick - odds {
font - size : 14 px ;
font - weight : 900 ;
color : var ( -- text - muted ) ;
}
. score - row {
display : flex ;
gap : 8 px ;
margin - top : 12 px ;
}
. score - row . compact {
margin - top : 8 px ;
}
. score - pill {
font - size : 13 px ;
font - weight : 800 ;
color : var ( -- text ) ;
padding : 5 px 10 px ;
background : rgba ( 255 , 255 , 255 , 0.04 ) ;
border - radius : 6 px ;
}
. score - pill em {
font - style : normal ;
font - size : 9 px ;
font - weight : 800 ;
color : var ( -- text - muted ) ;
letter - spacing : 0.06 em ;
text - transform : uppercase ;
text - transform : uppercase ;
margin - bottom : 10 px ;
margin - right : 5 px ;
}
. score - pill . dim { color : var ( -- text - muted ) ; }
. score - pill . sm { font - size : 11 px ; padding : 3 px 8 px ; }
. awaiting {
margin - top : 10 px ;
font - size : 12 px ;
color : var ( -- text - muted ) ;
font - style : italic ;
}
}
/* parlay legs */
/* parlay legs */
. legs - list {
. legs - list {
display : flex ;
display : flex ;
flex - direction : column ;
flex - direction : column ;
gap : 8 px ;
gap : 0 ;
}
}
. leg - row {
. leg - item {
display : flex ;
display : flex ;
justify - content : space - between ;
gap : 12 px ;
align - items : flex - start ;
padding : 12 px 0 ;
gap : 10 px ;
border - bottom : 1 px solid rgba ( 255 , 255 , 255 , 0.05 ) ;
background : # 1 a1a1a ;
border : 1 px solid # 252525 ;
border - radius : 9 px ;
padding : 10 px 12 px ;
}
}
. leg - row . won { border - color : rgba ( 61 , 184 , 101 , 0.2 ) ; }
. leg - item : last - child {
. leg - row . lost { border - color : rgba ( 224 , 80 , 80 , 0.18 ) ; }
border - bottom : none ;
padding - bottom : 0 ;
}
. leg - lef t {
. leg - status - do t {
flex - shrink : 0 ;
width : 22 px ;
height : 22 px ;
border - radius : 50 % ;
display : flex ;
display : flex ;
align - items : flex - start ;
align - items : center ;
gap : 8 px ;
justify - content : center ;
font - size : 10 px ;
font - weight : 900 ;
background : rgba ( 255 , 255 , 255 , 0.05 ) ;
border : 1.5 px solid var ( -- border ) ;
color : var ( -- text - muted ) ;
margin - top : 2 px ;
}
. leg - status - dot svg {
width : 12 px ;
height : 12 px ;
}
. leg - status - dot . won {
background : rgba ( 52 , 199 , 89 , 0.12 ) ;
border - color : rgba ( 52 , 199 , 89 , 0.45 ) ;
color : # 34 c759 ;
}
. leg - status - dot . lost {
background : rgba ( 255 , 69 , 58 , 0.1 ) ;
border - color : rgba ( 255 , 69 , 58 , 0.35 ) ;
color : var ( -- danger ) ;
}
. leg - content {
flex : 1 ;
flex : 1 ;
min - width : 0 ;
min - width : 0 ;
}
}
. leg - num {
. leg - head {
flex - shrink : 0 ;
width : 20 px ;
height : 20 px ;
border - radius : 50 % ;
background : # 2 a2a2a ;
color : # 666 ;
font - size : 10 px ;
font - weight : 800 ;
display : flex ;
display : flex ;
align - items : center ;
justify - content : space - between ;
justify - content : center ;
align - items : flex - start ;
margin - to p: 1 px ;
ga p: 8 px ;
}
margin - bottom : 4 px ;
. leg - num . won { background : rgba ( 61 , 184 , 101 , 0.18 ) ; color : # 3 db865 ; }
. leg - num . lost { background : rgba ( 224 , 80 , 80 , 0.18 ) ; color : # e05050 ; }
. leg - body {
display : flex ;
flex - direction : column ;
gap : 3 px ;
min - width : 0 ;
}
}
. leg - match {
. leg - match {
font - size : 12.5 px ;
font - size : 13 px ;
font - weight : 800 ;
font - weight : 800 ;
color : # d0d0d0 ;
color : var ( -- text ) ;
line - height : 1.35 ;
}
}
. leg - pick - line {
. leg - odds {
font - size : 11.5 px ;
color : # 888 ;
font - weight : 600 ;
}
. leg - scores {
display : flex ;
gap : 8 px ;
margin - top : 2 px ;
}
. leg - scores span {
font - size : 11 px ;
font - weight : 700 ;
color : # c8c8c8 ;
}
. leg - scores . muted - score {
color : # 555 ;
}
. leg - right {
flex - shrink : 0 ;
flex - shrink : 0 ;
display : fle x;
font - size : 13 p x;
flex - direction : column ;
align - items : flex - end ;
gap : 4 px ;
}
. leg - odds - val {
font - size : 15 px ;
font - weight : 900 ;
font - weight : 900 ;
color : # b8a04a ;
color : var ( -- text - muted ) ;
}
}
. leg - result - badge {
. leg - pick {
font - size : 9 px ;
font - size : 12 px ;
font - weight : 8 00;
font - weight : 6 00;
letter - spacing : 0.06 em ;
color : var ( -- text - muted ) ;
text - transform : uppercase ;
padding : 2 px 6 px ;
border - radius : 10 px ;
}
}
. leg - result - badge . won { color : # 3 db865 ; background : rgba ( 61 , 184 , 101 , 0.12 ) ; }
/* footer */
. leg - result - badge . los t { color : # e05050 ; background : rgba ( 224 , 80 , 80 , 0.1 ) ; }
. receipt - foo t {
. leg - result - badge . push { color : # 888 ; background : # 222 ; }
padding : 14 px 18 px 16 px ;
. leg - result - badge . pending { color : # 666 ; background : # 1 e1e1e ; }
/* summary */
. summary - rows {
display : flex ;
flex - direction : column ;
gap : 0 ;
}
}
. sum - row {
. foot - row {
display : flex ;
display : flex ;
justify - content : space - between ;
justify - content : space - between ;
align - items : center ;
align - items : center ;
padding : 8 px 0 ;
padding : 7 px 0 ;
border - bottom : 1 px solid # 1 c1c1c ;
font - size : 13 px ;
font - size : 13 px ;
color : # b0b0b0 ;
color : var ( -- text - muted ) ;
font - weight : 600 ;
font - weight : 600 ;
}
}
. sum - row : last - child {
. foot - row span : last - child {
border - bottom : none ;
color : var ( -- text ) ;
font - weight : 700 ;
}
}
. sum - row . mute d {
. foot - row . gol d {
color : # 444 ;
color : var ( -- primary - light ) ;
font - size : 11 px ;
}
. odds - val {
color : # b8a04a ;
font - weight : 800 ;
font - weight : 800 ;
}
}
. am t- won {
. foo t- row . id {
color : # 3 db865 ;
margin - top : 4 px ;
font - weight : 900 ;
padding - top : 10 px ;
font - size : 15 px ;
border - top : 1 px solid rgba ( 255 , 255 , 255 , 0.05 ) ;
font - size : 11 px ;
}
}
. am t- pending {
. foo t- row . id span : last - child {
color : # e8c84a ;
color : # 555 ;
font - weight : 9 00;
font - weight : 6 00;
}
}
. amt - lost {
. mono {
color : # e05050 ;
font - family : ui - monospace , monospace ;
font - weight : 900 ;
letter - spacing : 0.02 em ;
font - size : 15 px ;
}
. bet - no - val {
font - family : monospace ;
letter - spacing : 0.04 em ;
}
}
< / style >
< / style >