diff --git a/web/src/pages/chiamata.rs b/web/src/pages/chiamata.rs index e3aa365..c6c1563 100644 --- a/web/src/pages/chiamata.rs +++ b/web/src/pages/chiamata.rs @@ -464,10 +464,25 @@ pub fn chiamata_table( } /// Overlay for the auction winner: name the called card. +/// +/// The winning bid fixes the rank, so the choice is which of the four +/// suits to call: its holder becomes the secret partner, unless the +/// caller holds it too — then that option means playing alone. pub fn call_picker(game: GameView, socket: Signal>) -> View { let Some(cards) = game.callable_cards.clone().filter(|cards| !cards.is_empty()) else { return view! {}; }; + let rank = cards + .first() + .and_then(|code| code.get(..2)) + .and_then(|rank| rank.parse::().ok()) + .map(rank_name) + .unwrap_or_default(); + let title = if rank.is_empty() { + "Name the called card".to_string() + } else { + format!("You won with the {rank} — name the card") + }; let own_hand = game .players .iter() @@ -479,33 +494,35 @@ pub fn call_picker(game: GameView, socket: Signal>) -> View { .map(|code| { let held = own_hand.contains(&code); let label = card_label(&code); - let img = card_img(code.clone(), "hand-card"); - let solo_note = held.then(|| view! { - span(class="solo-note") { "plays alone" } - }); + let img = card_img(code.clone(), "call-card"); + let note = if held { + view! { span(class="call-note solo") { "Plays alone" } } + } else { + view! { span(class="call-note partner") { "Partner" } } + }; let click_code = code.clone(); view! { - button(class="capture-option call-option", on:click=move |_| { + button(class="call-option", on:click=move |_| { if let Some(s) = socket.get_clone() { s.call_card(&click_code); } }) { (img) span(class="call-label") { (label) } - (solo_note) + (note) } } }) .collect::>(); view! { div(class="overlay") { - div(class="picker") { - h3 { "Name the called card" } + div(class="picker call-picker") { + h3 { (title) } p(class="text-secondary small") { "Its holder becomes your secret partner; name one of your " "own cards to play alone." } - div(class="capture-options") { (options) } + div(class="call-options") { (options) } } } } diff --git a/web/style.css b/web/style.css index 2e9f133..2413ffd 100644 --- a/web/style.css +++ b/web/style.css @@ -292,8 +292,12 @@ body { inset: 0; background: rgba(0, 0, 0, 0.65); display: flex; - align-items: center; + align-items: flex-start; justify-content: center; + /* Tall dialogs (the chiamata call picker, the scoring summaries) can + * exceed short windows: allow scrolling instead of clipping them. */ + overflow-y: auto; + padding: 1rem; z-index: 20; } @@ -302,8 +306,11 @@ body { border-radius: 14px; padding: 1.5rem 2rem; text-align: center; - min-width: min(320px, calc(100vw - 1.5rem)); - max-width: calc(100vw - 1.5rem); + /* Centers the dialog when it fits and keeps it reachable (top + * aligned, scrollable via the overlay) when it does not. */ + margin: auto; + min-width: min(320px, calc(100vw - 3rem)); + max-width: calc(100vw - 3rem); } .capture-options { @@ -784,26 +791,64 @@ body { border-color: var(--text); } -/* The auction winner's call picker: one card per suit. */ -.call-option { - flex-direction: column; - align-items: center; +/* The auction winner's call picker: the four cards of the won rank, + * laid out in a compact grid so the dialog stays short. */ +.call-picker { + max-width: min(34rem, calc(100vw - 3rem)); } -.call-option .playing-card { +.call-options { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.5rem; + margin: 1rem 0 0; +} + +@media (min-width: 576px) { + .call-options { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} + +.call-option { + background: var(--panel-light); + border: 1px solid #4c6a54; + border-radius: 10px; + padding: 0.4rem 0.3rem; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.15rem; +} + +.call-option:hover { + border-color: var(--accent); +} + +/* Smaller than a hand card: the picker must fit short laptop windows. */ +.playing-card.call-card { + height: 84px; margin: 0 auto; } .call-label { display: block; - font-size: 0.85rem; + font-size: 0.8rem; color: var(--text); - margin-top: 0.25rem; + margin-top: 0.15rem; } -.solo-note { +.call-note { display: block; font-size: 0.7rem; +} + +.call-note.partner { + color: var(--team-a); +} + +.call-note.solo { color: var(--accent); }