View controls above the board, decisions below it, the account beside it
The board's controls had three homes: zoom in the corner, the eyes toggle floating above it, and the turn's actions wrapping into rows beneath. Now a toolbar hugs the board's top — First-person at the left; − · level · + · Fit at the right — and a dock sits beneath it in three groups: what the square offers, the tools (punch a wall, hints, discard) set quieter, and draw beside End turn pinned at the right. While a card is aimed the dock leads with the decision in a line — "Choose a destination · lit squares are within 4 · ends your movement" — and a cancel, so the lit squares always say what they mean. The panel beside the board keeps the card and the full account, now with the instruction first and discarding demoted to a quiet last resort. A phone keeps the offers and End turn in view and its tools behind More. With the hand at the left, the columns grow with the cards so they no longer overlap. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
648f6ab8cd
commit
7a6aa3d2a1
+175
-82
@@ -113,6 +113,13 @@
|
|||||||
* dimensions behind when the first-person pane closes. */
|
* dimensions behind when the first-person pane closes. */
|
||||||
let boardViewportEl = $state<HTMLDivElement | null>(null);
|
let boardViewportEl = $state<HTMLDivElement | null>(null);
|
||||||
let boardZoom = $state(1);
|
let boardZoom = $state(1);
|
||||||
|
/** Back to the whole maze at once. */
|
||||||
|
function resetZoom() {
|
||||||
|
boardZoom = 1;
|
||||||
|
if (boardViewportEl) requestAnimationFrame(() => { boardViewportEl!.scrollLeft = 0; boardViewportEl!.scrollTop = 0; });
|
||||||
|
}
|
||||||
|
/** The phone's dock keeps its secondary tools behind "More". */
|
||||||
|
let moreOpen = $state(false);
|
||||||
function zoomBoard(dir: number) {
|
function zoomBoard(dir: number) {
|
||||||
const el = boardViewportEl;
|
const el = boardViewportEl;
|
||||||
const prev = boardZoom;
|
const prev = boardZoom;
|
||||||
@@ -132,6 +139,8 @@
|
|||||||
if (!vb || !vb.width || !vb.height) return;
|
if (!vb || !vb.width || !vb.height) return;
|
||||||
const fitW = Math.min(el.clientWidth, (el.clientHeight * vb.width) / vb.height);
|
const fitW = Math.min(el.clientWidth, (el.clientHeight * vb.width) / vb.height);
|
||||||
el.style.setProperty("--fit-w", `${Math.max(0, fitW)}px`);
|
el.style.setProperty("--fit-w", `${Math.max(0, fitW)}px`);
|
||||||
|
// The toolbar above the board hugs the board's drawn width, not the column's.
|
||||||
|
el.closest<HTMLElement>(".board-zone")?.style.setProperty("--fit-w", `${Math.max(0, fitW)}px`);
|
||||||
}
|
}
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const el = boardViewportEl;
|
const el = boardViewportEl;
|
||||||
@@ -600,6 +609,25 @@
|
|||||||
const cellSelectMode = $derived(
|
const cellSelectMode = $derived(
|
||||||
(selectedCard != null && CELL_CARDS.has(selectedCard.cardId)) || pendingCellFor !== null,
|
(selectedCard != null && CELL_CARDS.has(selectedCard.cardId)) || pendingCellFor !== null,
|
||||||
);
|
);
|
||||||
|
/** The dock's leading line while a card is being aimed: the decision in
|
||||||
|
* a few words, beside the board where the tap happens. The panel beside
|
||||||
|
* the board keeps the card and the full account. */
|
||||||
|
const dockLead = $derived.by(() => {
|
||||||
|
if (!selectedCard || !selectedDef || youMustRespond) return null;
|
||||||
|
const id = selectedCard.cardId;
|
||||||
|
if (id === "teleport") return pendingTeleport
|
||||||
|
? "Tap the marked square again to jump · or pick another"
|
||||||
|
: "Choose a destination · lit squares are within 4 · ends your movement";
|
||||||
|
if (id === "mental-force") return "Choose the wizard, then where they go · within 3 walked squares";
|
||||||
|
if (EDGE_CARDS.has(id)) return "Tap a wall line · lit lines are legal";
|
||||||
|
if (cellSelectMode) return "Choose a square · lit squares are legal targets";
|
||||||
|
if (selectedDef.cardType === "attack") return selectedDef.adjacent
|
||||||
|
? "Choose a target · in your square or the one beside you"
|
||||||
|
: "Choose a target · lit squares are in your sight";
|
||||||
|
if (selectedDef.cardType === "number") return "A NUMBER · play it for movement, or tap a spell to power it";
|
||||||
|
if (selectedDef.los) return "Choose a target · lit squares are in your sight";
|
||||||
|
return `${selectedDef.name} · its panel offers the choices`;
|
||||||
|
});
|
||||||
const numberTotal = $derived(attachedNumber ? cardDef(attachedNumber.cardId).value! : 1);
|
const numberTotal = $derived(attachedNumber ? cardDef(attachedNumber.cardId).value! : 1);
|
||||||
|
|
||||||
function dispatch(command: Parameters<typeof net.command>[0]) {
|
function dispatch(command: Parameters<typeof net.command>[0]) {
|
||||||
@@ -2844,10 +2872,10 @@
|
|||||||
}}>Drop it here</button>
|
}}>Drop it here</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if selectedCard && isYourTurn && !youMustRespond}
|
{#if selectedCard && isYourTurn && !youMustRespond}
|
||||||
<button class="stamp tiny discard-one" onclick={() => {
|
<button class="hint-cancel discard-one" title="throw this card away instead of playing it" onclick={() => {
|
||||||
if (selectedCard) dispatch({ type: "discard", instanceIds: [selectedCard.instanceId] });
|
if (selectedCard) dispatch({ type: "discard", instanceIds: [selectedCard.instanceId] });
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}}>Discard it</button>
|
}}>discard it instead</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if selectedCard?.cardId === "add"}
|
{#if selectedCard?.cardId === "add"}
|
||||||
<span>— ADD rides a second NUMBER: play one number for movement,
|
<span>— ADD rides a second NUMBER: play one number for movement,
|
||||||
@@ -2870,14 +2898,25 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/snippet}
|
{/snippet}
|
||||||
<section class="board-zone">
|
<section class="board-zone">
|
||||||
{#if view.you && !net.spectating}
|
<!-- View controls above the board, in one row: the way of looking
|
||||||
<div class="view-toggle">
|
at the left, the zoom at the right. -->
|
||||||
<button class="stamp tiny" class:lit={prefs.liveFp} onclick={() => setPref("liveFp", !prefs.liveFp)}
|
<div class="board-tools">
|
||||||
title="a first-person pane above the board, through your wizard's eyes">
|
<div class="tools-left">
|
||||||
{prefs.liveFp ? "▦ the board only" : "👁 through your eyes"}
|
{#if view.you && !net.spectating}
|
||||||
</button>
|
<button class="tool" class:on={prefs.liveFp} aria-pressed={prefs.liveFp}
|
||||||
|
onclick={() => setPref("liveFp", !prefs.liveFp)}
|
||||||
|
title="a first-person pane above the board, through your wizard's eyes">
|
||||||
|
👁 First-person
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
<div class="tools-right" role="group" aria-label="board zoom">
|
||||||
|
<button class="tool" title="zoom out" onclick={() => zoomBoard(-1)} disabled={boardZoom <= 1}>−</button>
|
||||||
|
<span class="zoom-level" aria-live="polite">{Math.round(boardZoom * 100)}%</span>
|
||||||
|
<button class="tool" title="zoom in" onclick={() => zoomBoard(1)} disabled={boardZoom >= 4}>+</button>
|
||||||
|
<button class="tool" title="the whole maze at once" onclick={resetZoom} disabled={boardZoom === 1}>Fit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{#if stepCaption && me}
|
{#if stepCaption && me}
|
||||||
<div class="step-caption">
|
<div class="step-caption">
|
||||||
<span class="step-chip" style:background={playerColor(me.id)}></span>
|
<span class="step-chip" style:background={playerColor(me.id)}></span>
|
||||||
@@ -2928,10 +2967,6 @@
|
|||||||
povCreatureId={prefs.liveFp && !net.spectating ? fpvBody : null}
|
povCreatureId={prefs.liveFp && !net.spectating ? fpvBody : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="zoom-controls" role="group" aria-label="board zoom">
|
|
||||||
<button class="zoom-btn" title="zoom in" onclick={() => zoomBoard(1)} disabled={boardZoom >= 4}>+</button>
|
|
||||||
<button class="zoom-btn" title="zoom out" onclick={() => zoomBoard(-1)} disabled={boardZoom <= 1}>−</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -3209,6 +3244,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if selectedCard}
|
{:else if selectedCard}
|
||||||
|
{@render tableGuidance()}
|
||||||
<div class="cast-panel-card"><Card card={selectedCard} onfaq={(id) => (faqCardId = id)} /></div>
|
<div class="cast-panel-card"><Card card={selectedCard} onfaq={(id) => (faqCardId = id)} /></div>
|
||||||
{#if attachedNumber || attachedMods.length > 0}
|
{#if attachedNumber || attachedMods.length > 0}
|
||||||
<div class="cast-panel-sub">riding along</div>
|
<div class="cast-panel-sub">riding along</div>
|
||||||
@@ -3225,7 +3261,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{@render tableGuidance()}
|
{#if youMustRespond}{@render tableGuidance()}{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="chronicle" class:tucked={castPanelOn} aria-label="game log" bind:this={chronicleEl}
|
<div class="chronicle" class:tucked={castPanelOn} aria-label="game log" bind:this={chronicleEl}
|
||||||
@@ -3304,52 +3340,74 @@
|
|||||||
{@render tableGuidance()}
|
{@render tableGuidance()}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="actions">
|
<!-- The dock: decisions below the board. What the square offers at
|
||||||
{#if isYourTurn && onWarpToken}
|
the left, the tools in the middle, the turn's end at the right —
|
||||||
<button class="stamp" onclick={() => dispatch({ type: "warpStep" })}>Step through the warp</button>
|
and, while a card is aimed, the decision in one line on top. -->
|
||||||
|
<div class="dock" class:targeting={dockLead != null}>
|
||||||
|
{#if dockLead && isYourTurn}
|
||||||
|
<div class="dock-lead">
|
||||||
|
<span>{dockLead}</span>
|
||||||
|
{#if selectedCard?.cardId === "teleport" && pendingTeleport}
|
||||||
|
<button class="stamp tiny primary" onclick={confirmTeleport}>teleport!</button>
|
||||||
|
{/if}
|
||||||
|
<button class="hint-cancel dock-cancel" onclick={clearSelection}>cancel</button>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if isYourTurn}
|
{#if isYourTurn}
|
||||||
<button class="stamp" disabled={!treasureHere || carryingTreasure || view.turn.actionsEnded}
|
<div class="dock-row">
|
||||||
onclick={pickUp}>Pick up treasure</button>
|
<div class="dock-context">
|
||||||
{#if pickChoice}
|
{#if onWarpToken}
|
||||||
<span class="pick-which">
|
<button class="stamp" onclick={() => dispatch({ type: "warpStep" })}>Step through the warp</button>
|
||||||
— whose?
|
{/if}
|
||||||
{#each treasuresHere as t (t.id)}
|
<button class="stamp" disabled={!treasureHere || carryingTreasure || view.turn.actionsEnded}
|
||||||
<button class="stamp tiny" onclick={() => pickUpNamed(t.id)}>{t.owner}'s</button>
|
onclick={pickUp}>Pick up treasure</button>
|
||||||
|
{#if pickChoice}
|
||||||
|
<span class="pick-which">
|
||||||
|
— whose?
|
||||||
|
{#each treasuresHere as t (t.id)}
|
||||||
|
<button class="stamp tiny" onclick={() => pickUpNamed(t.id)}>{t.owner}'s</button>
|
||||||
|
{/each}
|
||||||
|
<button class="hint-cancel" onclick={() => (pickChoice = false)}>never mind</button>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
<button class="stamp" disabled={!carryingTreasure} onclick={drop}>Drop treasure</button>
|
||||||
|
{#each objectsHere as obj (obj.instanceId)}
|
||||||
|
<button class="stamp" disabled={view.turn.actionsEnded}
|
||||||
|
onclick={() => dispatch({ type: "pickUpObject", instanceId: obj.instanceId })}>
|
||||||
|
Pick up {cardDef(obj.cardId).name}</button>
|
||||||
{/each}
|
{/each}
|
||||||
<button class="hint-cancel" onclick={() => (pickChoice = false)}>never mind</button>
|
{#each tearTargets as t (t.id)}
|
||||||
</span>
|
<button class="stamp" disabled={view.turn.attackUsed || view.turn.round === 1}
|
||||||
{/if}
|
onclick={() => dispatch({ type: "tearTreasure", targetId: t.id })}>
|
||||||
<button class="stamp" disabled={!carryingTreasure} onclick={drop}>Drop treasure</button>
|
Tear the treasure from {t.id}'s arms</button>
|
||||||
{#each objectsHere as obj (obj.instanceId)}
|
{/each}
|
||||||
<button class="stamp" disabled={view.turn.actionsEnded}
|
</div>
|
||||||
onclick={() => dispatch({ type: "pickUpObject", instanceId: obj.instanceId })}>
|
<button class="stamp tiny more-btn" onclick={() => (moreOpen = !moreOpen)} aria-expanded={moreOpen}>
|
||||||
Pick up {cardDef(obj.cardId).name}</button>
|
{moreOpen ? "Less" : "More…"}</button>
|
||||||
{/each}
|
<div class="dock-tools" class:open={moreOpen}>
|
||||||
{#each tearTargets as t (t.id)}
|
<button class="stamp quiet" class:primary={punchWallMode} disabled={view.turn.attackUsed && !punchWallMode}
|
||||||
<button class="stamp" disabled={view.turn.attackUsed || view.turn.round === 1}
|
onclick={() => (punchWallMode = !punchWallMode)}>
|
||||||
onclick={() => dispatch({ type: "tearTreasure", targetId: t.id })}>
|
{punchWallMode ? "Click the wall to punch — or cancel" : "Punch a wall…"}</button>
|
||||||
Tear the treasure from {t.id}'s arms</button>
|
<button class="stamp quiet" onclick={askCouncil}>🎓 Hints</button>
|
||||||
{/each}
|
<button class="stamp quiet" onclick={startDiscardMode}>Discard cards…</button>
|
||||||
<button class="stamp" class:primary={punchWallMode} disabled={view.turn.attackUsed && !punchWallMode}
|
</div>
|
||||||
onclick={() => (punchWallMode = !punchWallMode)}>
|
<div class="dock-turn">
|
||||||
{punchWallMode ? "Click the wall to punch — or cancel" : "Punch a wall…"}</button>
|
<label class="inline draw-pick">
|
||||||
<button class="stamp" onclick={askCouncil}>🎓 Ask the automatons</button>
|
draw
|
||||||
<button class="stamp" onclick={startDiscardMode}>Discard cards…</button>
|
<select bind:value={drawCount}>
|
||||||
<label class="inline draw-pick">
|
<option value={0}>0</option>
|
||||||
draw
|
<option value={1}>1</option>
|
||||||
<select bind:value={drawCount}>
|
<option value={2}>2</option>
|
||||||
<option value={0}>0</option>
|
</select>
|
||||||
<option value={1}>1</option>
|
</label>
|
||||||
<option value={2}>2</option>
|
{#if net.pending}
|
||||||
</select>
|
<span class="sending" aria-live="polite">{Date.now() - net.pending.at > 4000 ? "still sending" : "sending"} {net.pending.label}…</span>
|
||||||
</label>
|
{:else if net.confirmed}
|
||||||
{#if net.pending}
|
<span class="sending ok" aria-live="polite">✓ {net.confirmed} confirmed</span>
|
||||||
<span class="sending" aria-live="polite">{Date.now() - net.pending.at > 4000 ? "still sending" : "sending"} {net.pending.label}…</span>
|
{/if}
|
||||||
{:else if net.confirmed}
|
<button class="stamp primary" onclick={endTurn} disabled={offline}>End turn</button>
|
||||||
<span class="sending ok" aria-live="polite">✓ {net.confirmed} confirmed</span>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
<button class="stamp primary" onclick={endTurn} disabled={offline}>End turn</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -3752,7 +3810,35 @@
|
|||||||
margin: 0.2rem 0 0.1rem;
|
margin: 0.2rem 0 0.1rem;
|
||||||
}
|
}
|
||||||
/* The eyes toggle floats over the board's corner so it costs the maze no height. */
|
/* The eyes toggle floats over the board's corner so it costs the maze no height. */
|
||||||
.view-toggle { position: absolute; top: 0.25rem; right: 0.25rem; z-index: 3; }
|
.board-tools {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin: 0 auto 0.35rem;
|
||||||
|
min-height: 1.9rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: max(var(--fit-w, 100%), 24rem);
|
||||||
|
}
|
||||||
|
.tools-left, .tools-right { display: flex; align-items: center; gap: 0.3rem; }
|
||||||
|
.tool {
|
||||||
|
height: 1.7rem;
|
||||||
|
min-width: 1.7rem;
|
||||||
|
padding: 0 0.55rem;
|
||||||
|
border: 1px solid #8a7a5e;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(233, 225, 203, 0.9);
|
||||||
|
color: #43331f;
|
||||||
|
font-family: "Oswald", sans-serif;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tool:disabled { opacity: 0.35; cursor: default; }
|
||||||
|
.tool.on { background: #43331f; color: #e9e1cb; border-color: #43331f; }
|
||||||
|
.zoom-level { font-family: "Courier Prime", monospace; font-size: 0.75rem; color: #c9bd9f; min-width: 2.6rem; text-align: center; }
|
||||||
.step-caption {
|
.step-caption {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
max-width: calc(100% - 11rem);
|
max-width: calc(100% - 11rem);
|
||||||
@@ -4047,7 +4133,6 @@
|
|||||||
.stamp.primary:hover:not(:disabled) { background: #5a4429; }
|
.stamp.primary:hover:not(:disabled) { background: #5a4429; }
|
||||||
.stamp.big { font-size: 0.95rem; padding: 0.6rem 1.3rem; }
|
.stamp.big { font-size: 0.95rem; padding: 0.6rem 1.3rem; }
|
||||||
.stamp.tiny { font-size: 0.62rem; padding: 0.22rem 0.5rem; box-shadow: 1px 1px 0 rgba(0,0,0,0.3); }
|
.stamp.tiny { font-size: 0.62rem; padding: 0.22rem 0.5rem; box-shadow: 1px 1px 0 rgba(0,0,0,0.3); }
|
||||||
.stamp.discard-one { background: #8a7a5e; }
|
|
||||||
.prefs-slip { text-align: left; }
|
.prefs-slip { text-align: left; }
|
||||||
.pref-row {
|
.pref-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -4165,7 +4250,7 @@
|
|||||||
* most six, and the hand must span them all to hold the column. */
|
* most six, and the hand must span them all to hold the column. */
|
||||||
grid-row: 1 / span 6;
|
grid-row: 1 / span 6;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 8.2rem);
|
grid-template-columns: repeat(2, calc(8.2rem * var(--card-zoom, 1)));
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
@@ -4213,26 +4298,6 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
.zoom-controls {
|
|
||||||
position: absolute;
|
|
||||||
right: 0.4rem;
|
|
||||||
bottom: 0.4rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.25rem;
|
|
||||||
}
|
|
||||||
.zoom-btn {
|
|
||||||
width: 1.7rem;
|
|
||||||
height: 1.7rem;
|
|
||||||
border: 1px solid #8a7a5e;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: rgba(233, 225, 203, 0.9);
|
|
||||||
color: #43331f;
|
|
||||||
font-size: 1.05rem;
|
|
||||||
line-height: 1;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.zoom-btn:disabled { opacity: 0.35; cursor: default; }
|
|
||||||
.paper-rail { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
|
.paper-rail { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
|
||||||
|
|
||||||
.slip {
|
.slip {
|
||||||
@@ -4263,7 +4328,7 @@
|
|||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
}
|
}
|
||||||
.offline-note { font-style: italic; color: #6b3a2f; }
|
.offline-note { font-style: italic; color: #6b3a2f; }
|
||||||
.game.offline .hand, .game.offline .board-zone, .game.offline .actions .stamp { pointer-events: none; opacity: 0.55; }
|
.game.offline .hand, .game.offline .board-zone, .game.offline .dock .stamp { pointer-events: none; opacity: 0.55; }
|
||||||
.sending {
|
.sending {
|
||||||
font-family: "Courier Prime", monospace;
|
font-family: "Courier Prime", monospace;
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
@@ -4461,7 +4526,30 @@
|
|||||||
color: #43331f;
|
color: #43331f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions { position: relative; display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; min-height: 2rem; }
|
/* The dock: one row, three homes. The square's offers at the left, the
|
||||||
|
* tools in the middle, the turn's end pinned at the right whatever wraps. */
|
||||||
|
.dock { position: relative; display: flex; flex-direction: column; gap: 0.45rem; min-height: 2.4rem; }
|
||||||
|
.dock-row { display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; }
|
||||||
|
.dock-context, .dock-tools { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
|
||||||
|
.dock-tools { padding-left: 0.55rem; border-left: 1px solid rgba(233, 225, 203, 0.25); }
|
||||||
|
.dock-turn { position: relative; margin-left: auto; display: flex; align-items: center; gap: 0.55rem; }
|
||||||
|
.stamp.quiet { background: rgba(233, 225, 203, 0.14); color: #e9e1cb; border-color: rgba(233, 225, 203, 0.55); box-shadow: none; }
|
||||||
|
.stamp.quiet:hover:not(:disabled) { background: rgba(233, 225, 203, 0.26); }
|
||||||
|
.more-btn { display: none; }
|
||||||
|
.dock-lead {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: rgba(233, 225, 203, 0.12);
|
||||||
|
border: 1px dashed rgba(233, 225, 203, 0.35);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.4rem 0.7rem;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
color: #e9e1cb;
|
||||||
|
}
|
||||||
|
.dock-cancel { margin-left: auto; }
|
||||||
|
.discard-one { margin-left: auto; opacity: 0.8; }
|
||||||
.draw-pick { color: #a49c86; font-size: 0.9rem; }
|
.draw-pick { color: #a49c86; font-size: 0.9rem; }
|
||||||
|
|
||||||
.attack-scrim {
|
.attack-scrim {
|
||||||
@@ -4787,6 +4875,11 @@
|
|||||||
.inspector-card { transform: scale(1.3); }
|
.inspector-card { transform: scale(1.3); }
|
||||||
.board-zone { order: 1; }
|
.board-zone { order: 1; }
|
||||||
.table-edge { order: 2; margin-top: 0.6rem; }
|
.table-edge { order: 2; margin-top: 0.6rem; }
|
||||||
|
/* The phone's dock stays compact: the offers and the turn's end in
|
||||||
|
view, the tools behind More. */
|
||||||
|
.more-btn { display: inline-block; }
|
||||||
|
.dock-tools { display: none; border-left: 0; padding-left: 0; flex-basis: 100%; }
|
||||||
|
.dock-tools.open { display: flex; }
|
||||||
.paper-rail { order: 3; margin-top: 0.9rem; }
|
.paper-rail { order: 3; margin-top: 0.9rem; }
|
||||||
.board-viewport {
|
.board-viewport {
|
||||||
height: 56dvh;
|
height: 56dvh;
|
||||||
|
|||||||
Reference in New Issue
Block a user