/*
 * Handheld polish, layered over the captain's Unified Base stylesheets.
 *
 * The destination is an iPhone app, and this module is the only place that
 * knows it. It is loaded last and it is additive: every file it corrects -
 * styles.css, styles/foundation.css, styles/game-shell.css,
 * styles/responsive.css, styles/motion.css, styles/state-surfaces.css - stays
 * byte-identical to the Unified Base original, so the design's identity is
 * still auditable against its source.
 *
 * It changes no colour, no type family and no ornament. What it changes is
 * what a 390pt viewport gets wrong: the notch and the home indicator, a hand
 * that scrolled instead of fitting, a seat name clipped to one letter, a
 * contract chip that hid its own doubling state, a turn that had to be
 * inferred, and touch targets under 44pt.
 */

/* --------------------------------------------------------------------------
 * 1. Safe areas.
 *
 * index.html asks for `viewport-fit=cover`, so on a notched iPhone the shell
 * starts under the status bar and ends under the home indicator. The Unified
 * Base already spends `--ui-safe-bottom`; the top and the sides were never
 * claimed, because a desktop browser has neither.
 * ------------------------------------------------------------------------ */

:root {
  --ui-safe-top: env(safe-area-inset-top, 0px);
  --ui-safe-left: env(safe-area-inset-left, 0px);
  --ui-safe-right: env(safe-area-inset-right, 0px);
}

@media (max-width: 760px) {
  .ui-direction-a .shell {
    padding-top: calc(6px + var(--ui-safe-top));
    padding-left: max(6px, var(--ui-safe-left));
    padding-right: max(6px, var(--ui-safe-right));
  }

  body.ui-direction-a.in-game .shell {
    padding-top: calc(4px + var(--ui-safe-top));
    padding-left: max(4px, var(--ui-safe-left));
    padding-right: max(4px, var(--ui-safe-right));
  }

  .ui-direction-a .system-notice {
    top: calc(70px + var(--ui-safe-top));
  }
}

/* --------------------------------------------------------------------------
 * 2. The mat, and the dead band under it.
 *
 * The Unified Base sizes the mat off the *width* it can spare beside two side
 * panels. At 390pt there are no side panels, so the same arithmetic left a
 * 200pt band of empty felt between the mat and the hand. One custom property
 * now carries the mat's centre, and everything anchored to it - the two side
 * seats and the exposed card - reads that property instead of repeating 38%.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px) {
  .ui-direction-a .table-wrap {
    --mat-center: 43%;
    --mat-size: min(92vw, calc(100svh - 318px), 430px);
  }

  .ui-direction-a .center-table {
    top: var(--mat-center);
    width: var(--mat-size);
  }

  /* The phase chip sat at a fixed 58px from the top of the board, which was
     inside the Unified Base's smaller mat and is now above the enlarged one,
     on top of the partner's name. It rides the mat instead. */
  .ui-direction-a .table-core {
    top: calc(var(--mat-center) - var(--mat-size) / 2 + 16px);
  }

  .ui-direction-a .exposed-card-view {
    top: calc(var(--mat-center) + 12px);
  }

  /* The two side seats ride the mat's upper corners rather than its waist.
     At 390pt the mat is nearly the whole width, so a seat parked at the mat's
     centre puts its name pill straight through the card that seat just
     played - true of the Unified Base's own mobile layout, and more true once
     the mat grows. Lifting them clears the trick entirely and reads like a
     table seen from the near edge. */
  .ui-direction-a .seat-left,
  .ui-direction-a .seat-right {
    top: calc(var(--mat-center) - 92px);
  }

  /* A bigger mat can afford bigger trick cards; the trick is the thing being
     read, and 54pt was small enough to need a second look. */
  .ui-direction-a .play-slot .card {
    width: clamp(58px, 17vw, 74px);
  }

  .ui-direction-a .play-slot {
    min-width: 60px;
    min-height: 86px;
  }

  .ui-direction-a .exposed-card-view .card {
    width: 72px;
  }
}

@media (max-width: 760px) and (max-height: 700px) {
  .ui-direction-a .table-wrap {
    --mat-center: 39%;
    --mat-size: min(88vw, calc(100svh - 300px), 340px);
  }
}

/* --------------------------------------------------------------------------
 * 3. The board takes the space that is left, rather than guessing at it.
 *
 * `.shell` is a 100svh column and `.game` was sized by subtracting the topbar
 * and a hand-written constant from 100svh - which is only right while every
 * other term in that column is exactly what the constant assumed. Two of them
 * are not: `--ui-topbar-height` is the topbar's `min-height`, not its height
 * (61pt against a 56pt token), and section 1 above claims the notch as shell
 * padding, which the constant never paid for. The board therefore ran past the
 * foot of the screen by the whole top inset, and the hand - anchored to the
 * board's bottom, not the viewport's - was clipped into the home indicator.
 *
 * A flex column removes the arithmetic instead of correcting it: the topbar
 * takes its own height, and the board takes what is left, whatever the insets
 * and the type turn out to measure. The bottom inset stays unspent here on
 * purpose - the board reaches the glass, and `.seat-bottom` and `.bottom-bar`
 * each clear the home indicator themselves.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px), (orientation: landscape) and (max-height: 520px) and (max-width: 1099px) {
  body.ui-direction-a.in-game .shell {
    height: 100svh;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  body.ui-direction-a.in-game .game {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
  }
}

/* --------------------------------------------------------------------------
 * 4. The hand fits, instead of scrolling.
 *
 * A hand is eight cards and a phone is 390pt wide, so the Unified Base's
 * mobile fallback made the hand a horizontal scroller: you could not see the
 * cards you were choosing between without dragging, and the cards you could
 * see were flush against one edge. A card game's hand is a single glance.
 *
 * The overlap is exact rather than guessed. A percentage margin resolves
 * against the hand's own content box, so
 *
 *     margin = (100% - count x card-width) / (count - 1)
 *
 * is the precise per-card offset that makes `count` cards span the hand, and
 * `clamp` caps it at the design's 4px gap once the hand is small enough not to
 * need any overlap at all. `--hand-count` is the one thing CSS cannot derive;
 * `app.js:applyBottomFan` sets it beside the fan rotation it already writes.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px), (pointer: coarse) {
  .ui-direction-a .seat-bottom .hand {
    --hand-card-w: 58px;
    --hand-gap: 4px;
    /* A 58pt card is 87pt tall and the fan lifts the outer ones further, which
       the Unified Base's 92pt row clipped against the foot of the screen on a
       phone with no home-indicator inset to spend. */
    min-height: 100px;
    justify-content: center;
    justify-content: safe center;
    gap: 0;
    overflow-x: clip;
    overflow-y: visible;
  }

  .ui-direction-a .seat-bottom .hand .card + .card {
    margin-left: clamp(
      -34px,
      calc((100% - var(--hand-card-w) * var(--hand-count, 1)) / max(1, var(--hand-count, 1) - 1)),
      var(--hand-gap)
    );
  }

  /* Overlapping cards paint in DOM order, so the one being chosen has to come
     forward or the choice is hidden under its neighbour. */
  .ui-direction-a .seat-bottom .hand .card.selected,
  .ui-direction-a .seat-bottom .hand .card.premove,
  .ui-direction-a .seat-bottom .hand .card.play-pending {
    z-index: 6;
  }

  .ui-direction-a .seat-bottom .hand .card:active {
    z-index: 5;
  }

  /* A 58pt-wide card overlapped to 40pt is still a 58pt tap target, because
     the later card only covers it visually - but the *last* sliver of each
     covered card is what the thumb lands on, so lift the chosen one further
     than the desktop's 14px to keep the choice legible while it is pending. */
  .ui-direction-a .seat-bottom .card.selected {
    transform: translateY(-20px) !important;
  }
}

/* The card lift that answers a press exists only under `hover: hover and
   pointer: fine` (styles/motion.css), so a touch got no acknowledgement at all
   between the tap and the render. This is that acknowledgement, and it is the
   same lift. */
@media (hover: none) {
  .ui-direction-a .card.playable:active:not(.selected):not([disabled]) {
    z-index: 40;
    transform: translateY(-12px) scale(1.02) !important;
    border-color: var(--ui-brass-300);
    box-shadow:
      0 0 0 3px color-mix(in srgb, var(--ui-brass-300) 42%, transparent),
      0 18px 30px rgba(0, 0, 0, 0.48);
  }
}

/* --------------------------------------------------------------------------
 * 5. Seat identity.
 *
 * `سيكل ٢` rendered as `س...`: the seat copy was capped at 78pt and the
 * dealer badge took 44 of them. The name is the label a player checks before
 * every trick, so it gets the room and the badge wraps under it.
 *
 * The wrap is not conditional on the viewport - the desktop shows the same
 * seat as `سيك...` for the same reason, and a seat whose name is one letter is
 * a defect at any width.
 * ------------------------------------------------------------------------ */

.ui-direction-a .seat-left .seat-title,
.ui-direction-a .seat-right .seat-title,
.ui-direction-a .seat-top .seat-title {
  flex-wrap: wrap;
  justify-content: center;
  row-gap: 2px;
}

@media (max-width: 760px) {
  .ui-direction-a .seat-bottom {
    bottom: calc(10px + var(--ui-safe-bottom));
  }

  .ui-direction-a .seat-left,
  .ui-direction-a .seat-right {
    width: 132px;
  }

  .ui-direction-a .seat-left {
    left: 2px;
  }

  .ui-direction-a .seat-right {
    right: 2px;
  }

  .ui-direction-a .seat-left .seat-copy,
  .ui-direction-a .seat-right .seat-copy,
  .ui-direction-a .seat-top .seat-copy {
    max-width: 116px;
    padding: 4px 7px;
  }

  .ui-direction-a .seat-top .seat-title,
  .ui-direction-a .seat-left .seat-title,
  .ui-direction-a .seat-right .seat-title {
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 2px;
  }

  .ui-direction-a .seat-top .seat-title span:first-child,
  .ui-direction-a .seat-left .seat-title span:first-child,
  .ui-direction-a .seat-right .seat-title span:first-child {
    max-width: 104px;
  }
}

@media (max-width: 430px) {
  .ui-direction-a .seat-left,
  .ui-direction-a .seat-right {
    width: 126px;
  }

  /* The Unified Base clipped these to 58pt at 430. `دبل · مقفل` is the whole
     point of the badge, so it wraps rather than losing its second half. */
  .ui-direction-a .buyer-badge,
  .ui-direction-a .declarer-badge {
    max-width: 104px;
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
  }
}

/* --------------------------------------------------------------------------
 * 6. The contract chip says the whole contract.
 *
 * `حكم ♠ · دبل · مقفل ×2` was ellipsised to `حكم ♠ · دبل · مقفل...`, which
 * drops the multiplier - the one number that decides what the hand is worth.
 * The chip keeps the Unified Base's width, because the connection lamp is
 * parked beside it, and spends a second line instead.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px) {
  .ui-direction-a .score-grid .score-contract {
    min-width: 108px;
  }

  .ui-direction-a .score-grid .score-contract strong {
    max-width: 100%;
    overflow: visible;
    font-size: 11px;
    line-height: 1.25;
    white-space: normal;
    text-overflow: clip;
  }

  .ui-direction-a .declarer-mini {
    max-width: 108px;
    font-size: 8.5px;
  }
}

/* --------------------------------------------------------------------------
 * 7. Whose turn it is.
 *
 * On a desktop the answer is the seat ring plus a side panel that names the
 * turn. On a phone both side panels are gone and the ring was a 13%-opacity
 * halo, so the turn had to be inferred from which seat had not played. The
 * ring becomes a brass pulse, and the viewer's own turn lights the hand's
 * baseline - the design's own accent, louder.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px) {
  .ui-direction-a .seat.active .seat-head {
    border-color: var(--ui-brass-400);
    box-shadow:
      0 0 0 3px color-mix(in srgb, var(--ui-brass-400) 26%, transparent),
      0 0 18px color-mix(in srgb, var(--ui-brass-400) 22%, transparent),
      var(--ui-shadow-xs);
    animation: ui-seat-turn 2000ms var(--ui-ease-standard) infinite;
  }

  .ui-direction-a .seat.active .seat-avatar {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--ui-brass-300) 46%, transparent);
  }

  body.ui-direction-a.is-your-turn .seat-bottom .hand {
    border-radius: var(--ui-radius-md) var(--ui-radius-md) 0 0;
    background:
      linear-gradient(0deg, color-mix(in srgb, var(--ui-brass-500) 13%, transparent), transparent 62%);
    box-shadow: inset 0 -2px 0 color-mix(in srgb, var(--ui-brass-400) 52%, transparent);
  }

  body.ui-direction-a.is-your-turn .table-core {
    border-color: var(--ui-line-brass);
    background: color-mix(in srgb, var(--ui-brass-500) 16%, var(--ui-bg-deep));
  }

  body.ui-direction-a.is-your-turn .table-core strong {
    color: var(--ui-brass-200);
  }
}

@keyframes ui-seat-turn {
  0%, 100% {
    box-shadow:
      0 0 0 3px color-mix(in srgb, var(--ui-brass-400) 26%, transparent),
      0 0 18px color-mix(in srgb, var(--ui-brass-400) 22%, transparent),
      var(--ui-shadow-xs);
  }

  50% {
    box-shadow:
      0 0 0 5px color-mix(in srgb, var(--ui-brass-400) 12%, transparent),
      0 0 26px color-mix(in srgb, var(--ui-brass-400) 34%, transparent),
      var(--ui-shadow-xs);
  }
}

/* --------------------------------------------------------------------------
 * 8. The end of a hand, and the end of a match, as moments.
 *
 * The outcome panel floated over a live table with nothing behind it, so a
 * result read as another overlay rather than as a stop. A scrim settles the
 * table under it; the panel keeps its own entrance.
 * ------------------------------------------------------------------------ */

body.ui-direction-a.has-outcome .table-board::before {
  content: "";
  position: absolute;
  z-index: 80;
  inset: 0;
  /* styles.css parks this pseudo-element with `display: none`. */
  display: block;
  pointer-events: none;
  background: radial-gradient(72% 52% at 50% 50%, rgba(3, 6, 5, 0.62), rgba(3, 6, 5, 0.84));
  animation: ui-outcome-scrim 220ms var(--ui-ease-out) both;
}

@keyframes ui-outcome-scrim {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@media (max-width: 760px) {
  .ui-direction-a .outcome-panel {
    /* Optically centred in the viewport rather than in the board, which starts
       below the topbar and so reads high by half of it. */
    top: 47%;
    width: min(340px, calc(100% - 22px));
    padding: 20px 16px;
    gap: 8px;
  }

  .ui-direction-a .outcome-actions button {
    min-height: 48px;
    font-size: 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ui-direction-a .seat.active .seat-head,
  body.ui-direction-a.has-outcome .table-board::before {
    animation: none !important;
  }
}

/* --------------------------------------------------------------------------
 * 9. Touch targets.
 *
 * Apple's floor is 44pt. These are the controls that were under it on a
 * 390pt viewport; each keeps its own look and only grows its hit area.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px) {
  .ui-direction-a .system-notice-actions button {
    min-height: 44px;
    padding-inline: 14px;
  }

  body.ui-direction-a.in-game .icon-button,
  body.ui-direction-a.is-waiting #copyInviteButton {
    width: 44px;
    min-width: 44px;
    height: 44px;
    min-height: 44px;
  }

  .ui-direction-a .join-row button,
  .ui-direction-a .waiting-home-button,
  .ui-direction-a .waiting-room-card button {
    min-height: 48px;
  }

  .ui-direction-a .bottom-bar .phase-action {
    min-height: 52px;
  }
}

/* --------------------------------------------------------------------------
 * 10. What the phone loses with the side panels.
 *
 * Both `.side` columns are `display: none` under 1100px, and with them go the
 * round log, the projects card and the majlis list - so a phone plays a trick
 * without being told which trick it is, what the projects stand at, or whether
 * the contract is doubled. The band of felt between the mat and the hand,
 * which exists only while the action rail is down, is exactly where that
 * belongs.
 *
 * No new markup: `.details-grid` is already in the DOM and `app.js` already
 * keeps all seven of its cells current (`app.js:584-595`). Four of them are
 * the four a player asks about mid-trick; the rest stay hidden, and the whole
 * strip stays hidden whenever the rail is up or a hand has ended.
 *
 * `.side` is hidden by width and this strip replaces it, so it has to be
 * restored across every viewport that loses the panels - which is both of the
 * shapes a phone has. Portrait has a band of felt under the mat to put it in;
 * landscape has none, and puts it in the corner beside the hand instead. Only
 * the placement differs, so only the placement is written twice.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px) {
  body.ui-direction-a.in-game[data-phase="PLAY_TRICKS"]:not(.has-canonical-phase-actions):not(.has-outcome) .score-panel {
    top: calc(var(--mat-center) + var(--mat-size) / 2 + 38px);
    bottom: auto;
    z-index: 20;
    width: min(354px, calc(100% - 20px));
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    justify-items: stretch;
  }
}

/* At 844x390 the mat runs from just under the topbar to just above the hand,
   so the strip goes where landscape has room and portrait does not: the corner
   the hand's fan never reaches, on the side an Arabic reader starts from. */
@media (orientation: landscape) and (max-height: 520px) and (max-width: 1099px) {
  body.ui-direction-a.in-game[data-phase="PLAY_TRICKS"]:not(.has-canonical-phase-actions):not(.has-outcome) .score-panel {
    top: auto;
    bottom: calc(8px + var(--ui-safe-bottom));
    left: auto;
    right: max(8px, var(--ui-safe-right));
    z-index: 20;
    width: min(268px, calc(50% - 150px));
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    justify-items: stretch;
    transform: none;
  }
}

@media (max-width: 760px), (orientation: landscape) and (max-height: 520px) and (max-width: 1099px) {
  .ui-direction-a .score-panel .room-card {
    display: none;
  }

  .ui-direction-a .score-panel .details-grid {
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 4px;
    padding: 8px 6px;
    border: 1px solid var(--ui-line);
    border-radius: var(--ui-radius-sm);
    background:
      linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0) 60%),
      rgba(7, 11, 10, 0.82);
    box-shadow: var(--ui-inset-highlight), var(--ui-shadow-xs);
  }

  .ui-direction-a .score-panel .details-grid > div {
    display: none;
    min-width: 0;
    text-align: center;
  }

  /* الدور, الأكلة, المشاريع, الدبل - in that reading order, right to left. */
  .ui-direction-a .score-panel .details-grid > div:nth-child(6) {
    order: 1;
    display: block;
  }

  .ui-direction-a .score-panel .details-grid > div:nth-child(7) {
    order: 2;
    display: block;
  }

  .ui-direction-a .score-panel .details-grid > div:nth-child(4) {
    order: 3;
    display: block;
  }

  .ui-direction-a .score-panel .details-grid > div:nth-child(3) {
    order: 4;
    display: block;
  }

  .ui-direction-a .score-panel .details-grid span {
    font-size: 8.5px;
    letter-spacing: 0.02em;
  }

  .ui-direction-a .score-panel .details-grid strong {
    margin-top: 3px;
    overflow: hidden;
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Two columns in landscape, not four: a quarter of 268pt cannot hold
   `لاعب سيكل`, and the corner has the height to spend that the portrait band
   does not. This has to follow the shared block, which sets the four. */
@media (orientation: landscape) and (max-height: 520px) and (max-width: 1099px) {
  .ui-direction-a .score-panel .details-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 5px 10px;
    padding: 8px 10px;
  }
}

/* --------------------------------------------------------------------------
 * 11. The partner's project bubble, which had nowhere to go but up.
 *
 * A seat's speech bubble grows upward from the seat's top edge
 * (`styles.css:1709`, `translate(-50%, -100%)`), and the project variant is
 * ~95pt tall because it carries the declared cards. The top seat sits 48pt
 * below the board, and the board starts just under the topbar - so that one
 * bubble drew straight through the topbar and was cut in half. This is not new
 * in this branch: it is the same on the Unified Base layer alone, at any
 * viewport, whenever the partner declares a project.
 *
 * On a handheld there is no room above that seat, and there is room below it -
 * the mat starts 11pt further down. So the top seat's card-carrying bubble
 * hangs under its own name pill instead, over the mat's upper rim, and its
 * tail flips to point back up at the seat it belongs to.
 * ------------------------------------------------------------------------ */

@media (max-width: 760px), (orientation: landscape) and (max-height: 520px) and (max-width: 1099px) {
  .ui-direction-a .seat-top .seat-speech.has-cards {
    top: 100%;
    transform: translate(-50%, 0);
  }

  .ui-direction-a .seat-top .seat-speech.has-cards::after {
    top: 0;
    transform: translate(-50%, -50%) rotate(225deg);
  }
}

/* --------------------------------------------------------------------------
 * 12. The wait before a match.
 *
 * `createRoom` deals a hand and drives three automated seats before it answers.
 * On a phone that is seconds of a page that looks frozen, because the only
 * feedback was `disabled`. `app.js:setLobbyBusy` marks the pressed button and
 * swaps its subtitle; this is the sweep that says the wait is progress.
 * ------------------------------------------------------------------------ */

.ui-direction-a .mode-button.is-busy {
  border-color: var(--ui-line-brass);
}

.ui-direction-a .mode-button.is-busy small {
  color: var(--ui-brass-300);
}

.ui-direction-a .mode-button.is-busy::before {
  content: "";
  position: absolute;
  z-index: 3;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(
    100deg,
    transparent 32%,
    color-mix(in srgb, var(--ui-brass-300) 16%, transparent) 50%,
    transparent 68%
  );
  background-size: 260% 100%;
  animation: ui-lobby-wait 1150ms linear infinite;
}

.ui-direction-a button:disabled:not(.is-busy) {
  opacity: 0.58;
}

@keyframes ui-lobby-wait {
  from {
    background-position: 140% 0;
  }

  to {
    background-position: -140% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ui-direction-a .mode-button.is-busy::before {
    animation: none;
  }
}

/* --------------------------------------------------------------------------
 * 13. Behaving like an app rather than a web page.
 *
 * These are the four things iOS Safari does to a page that an app must not do:
 * wait 300ms after a tap in case a second one is a zoom, rubber-band the whole
 * board when a drag runs past the end of it, select the Arabic labels on a
 * long press, and offer to save a card face because the card is an image
 * inside a button. The name field is the one place a selection is wanted, so
 * it keeps it.
 * ------------------------------------------------------------------------ */

html.ui-direction-a,
body.ui-direction-a {
  overscroll-behavior: none;
}

body.ui-direction-a button,
body.ui-direction-a .card {
  touch-action: manipulation;
}

body.ui-direction-a.in-game {
  -webkit-user-select: none;
  user-select: none;
}

body.ui-direction-a .card,
body.ui-direction-a .card img {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  user-select: none;
}

body.ui-direction-a input,
body.ui-direction-a textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* --------------------------------------------------------------------------
 * 14. The phone turned sideways.
 *
 * An iPhone in landscape is 844x390, which is wider than the Unified Base's
 * 760px handheld breakpoint and shorter than anything the desktop layout was
 * sized for: the mat collapsed to `100svh - 258px` - 132pt - and the hand drew
 * straight through the partner's fan. It was the one viewport where the page
 * did not work at all.
 *
 * The fix is the same layout, budgeted for 390pt of height instead of width:
 * a shorter topbar, a mat sized off the height it actually has, the two side
 * seats moved out beside it into the width landscape has to spare, and a rail
 * that becomes a strip along the foot of the table. It is scoped by height, so
 * no desktop window that is merely wide reaches it.
 * ------------------------------------------------------------------------ */

@media (orientation: landscape) and (max-height: 520px) and (max-width: 1099px) {
  :root {
    --ui-topbar-height: 46px;
    --ui-page-gap: 4px;
  }

  .ui-direction-a .shell {
    padding: calc(3px + var(--ui-safe-top)) max(4px, var(--ui-safe-right))
      calc(4px + var(--ui-safe-bottom)) max(4px, var(--ui-safe-left));
  }

  body.ui-direction-a.in-game .shell {
    padding-top: calc(2px + var(--ui-safe-top));
    /* In game the board reaches the glass and the hand and the rail each spend
       `--ui-safe-bottom` themselves, so the shell must not spend it twice. */
    padding-bottom: 4px;
  }

  .ui-direction-a .topbar {
    flex: none;
  }

  .ui-direction-a .topbar {
    top: 0;
    min-height: var(--ui-topbar-height);
    gap: 6px;
    padding: 3px 8px;
    border-radius: var(--ui-radius-sm);
  }

  .ui-direction-a .topbar .topbar-score {
    min-height: 40px;
  }

  .ui-direction-a .score-grid > div {
    padding: 2px 8px;
  }

  .ui-direction-a .game {
    margin-top: 3px;
  }

  .ui-direction-a .game-layout,
  .ui-direction-a .table-wrap,
  .ui-direction-a .table-board {
    height: 100%;
    min-height: 0;
  }

  .ui-direction-a .table-wrap {
    --mat-center: 34%;
    --mat-size: min(26vw, calc(100svh - 230px), 162px);
  }

  .ui-direction-a .center-table {
    top: var(--mat-center);
    width: var(--mat-size);
  }

  .ui-direction-a .table-core {
    top: 2px;
    width: min(200px, 32%);
    min-height: 30px;
    padding: 4px 8px;
  }

  .ui-direction-a .seat-top {
    top: -6px;
    width: min(200px, 30%);
  }

  /* The partner is the one seat with no room above the mat, so its fan hung
     into the table and drew over the trick. The fan goes; the count badge it
     illustrates stays, and the trick is unobstructed. The two side seats keep
     theirs - landscape has the width for them. */
  .ui-direction-a .seat-top .hand {
    display: none;
  }

  .ui-direction-a .seat-top .seat-avatar,
  .ui-direction-a .seat-left .seat-avatar,
  .ui-direction-a .seat-right .seat-avatar {
    width: 28px;
    height: 28px;
  }

  .ui-direction-a .seat-top .card.back,
  .ui-direction-a .seat-left .card.back,
  .ui-direction-a .seat-right .card.back {
    width: 21px;
    height: auto;
    margin-left: -21px;
  }

  /* Landscape is the one shape with width to spare, so the side seats go back
     beside the mat instead of on top of it. */
  .ui-direction-a .seat-left,
  .ui-direction-a .seat-right {
    top: var(--mat-center);
    width: 152px;
  }

  .ui-direction-a .seat-left {
    left: max(8px, calc(50% - var(--mat-size) / 2 - 160px));
  }

  .ui-direction-a .seat-right {
    right: max(8px, calc(50% - var(--mat-size) / 2 - 160px));
  }

  .ui-direction-a .seat-left .seat-copy,
  .ui-direction-a .seat-right .seat-copy,
  .ui-direction-a .seat-top .seat-copy {
    max-width: 132px;
  }

  .ui-direction-a .seat-left .seat-title span:first-child,
  .ui-direction-a .seat-right .seat-title span:first-child,
  .ui-direction-a .seat-top .seat-title span:first-child {
    max-width: 118px;
  }

  .ui-direction-a .seat-bottom {
    top: auto;
    bottom: calc(2px + var(--ui-safe-bottom));
    width: calc(100% - 8px);
  }

  .ui-direction-a .seat-bottom .hand {
    --hand-card-w: 52px;
    min-height: 82px;
    padding: 2px 8px 0;
  }

  .ui-direction-a .seat-bottom .card {
    width: 52px;
    min-width: 52px;
  }

  .ui-direction-a .play-slot .card {
    width: clamp(36px, 5vw, 44px);
  }

  .ui-direction-a .play-slot {
    min-width: 38px;
    min-height: 54px;
  }

  .ui-direction-a .exposed-card-view {
    top: calc(var(--mat-center) + 12px);
  }

  .ui-direction-a .exposed-card-view .card {
    width: 48px;
  }

  .ui-direction-a .bottom-bar {
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    min-height: 0;
    max-height: 82px;
    padding: 5px 8px calc(5px + var(--ui-safe-bottom));
    transform: none;
    border-right: 0;
    border-bottom: 0;
    border-left: 0;
    border-radius: var(--ui-radius-md) var(--ui-radius-md) 0 0;
  }

  .ui-direction-a .bottom-bar .phase-action {
    min-height: 44px;
    padding: 6px 10px;
    font-size: 13px;
  }

  body.ui-direction-a[data-phase="BID_FIRST"] .seat-bottom,
  body.ui-direction-a[data-phase="BID_SECOND"] .seat-bottom,
  body.ui-direction-a[data-phase="PREPLAY_DOUBLE"] .seat-bottom,
  body.ui-direction-a.has-canonical-phase-actions[data-phase="PROJECT_OPPORTUNITY"] .seat-bottom,
  body.ui-direction-a.has-canonical-phase-actions[data-phase="PLAY_TRICKS"] .seat-bottom {
    bottom: calc(66px + var(--ui-safe-bottom));
  }

  /* While the rail is up it carries the viewer's own name pill, and 390pt of
     height has no room to say it twice. The design already fades this one out
     during the auction (styles.css:2083); landscape takes back its 45pt too,
     which is exactly what keeps the hand clear of the mat. */
  body.ui-direction-a[data-phase="BID_FIRST"] .seat-bottom .seat-head,
  body.ui-direction-a[data-phase="BID_SECOND"] .seat-bottom .seat-head,
  body.ui-direction-a[data-phase="PREPLAY_DOUBLE"] .seat-bottom .seat-head,
  body.ui-direction-a.has-canonical-phase-actions[data-phase="PROJECT_OPPORTUNITY"] .seat-bottom .seat-head,
  body.ui-direction-a.has-canonical-phase-actions[data-phase="PLAY_TRICKS"] .seat-bottom .seat-head {
    display: none;
  }

  .ui-direction-a .outcome-panel {
    top: 46%;
    max-height: calc(100svh - 84px - var(--ui-safe-bottom));
    padding: 12px 16px;
    gap: 4px;
    overflow-y: auto;
  }

  .ui-direction-a .outcome-panel h2 {
    font-size: 24px;
  }
}
