/* ===========================================================================
   The analyzer, reshaped for the iOS app.
   ===========================================================================

   The web analyzer is a board with a side panel, and on a phone that panel
   collapses into a stack of rows under the board: a move ticker, a step-arrow
   row, a header carrying two tabs plus four controls, a caption, and a "Back to
   game" button. Every one of them earns its place on a desktop. Together, on a
   390px screen, they leave the board about half the height it could have and
   give the player six places to look.

   This file is the app's answer, packed from the top of a screen that cannot
   scroll: the evaluation as a rule the full width of the display with the
   reading, the two board controls and the way out on a line under it, the board
   itself as large as what is left allows, two one-line player rows around it,
   the move list filling whatever the square board could not use, and ONE bar at
   the foot holding the actions a thumb repeats - the same shape as the tab bar
   the player already knows from everywhere else in the app. While a wall is
   being placed that bar becomes Rotate / Place / Cancel.

   Nothing here is loaded by the website: every rule is gated on
   `body.app-analyzer`, which js/analyzer-app.js sets only when Net.isNative()
   is true. The web analyzer is untouched.

   Companion: js/analyzer-app.js, which builds the two bars and keeps them in
   step with analysis.js. Read that file's header for how the two talk.
   ======================================================================== */

/* --- the two bars this file adds ---------------------------------------
   Real heights are published from JS (they depend on the safe-area inset,
   which CSS can read but cannot do arithmetic on across elements). These are
   the fallbacks used for the first paint, before the observer fires. */
body.app-analyzer {
  --aa-top-h: 71px;
  --aa-tab-h: 76px;
  /* The two seat rows plus the gaps around them. Kept here rather than inlined
     into the board calc so the seat padding below and this number can only ever
     be changed together. */
  --aa-seats-h: 68px;
}

/* =======================================================================
   1. What the analyzer screen stops showing
   =======================================================================
   The app's analyzer is one screen with one job, so everything that is a
   second way to do something already on it goes. Listed one per line with
   the reason, because "why is this hidden" is the question this file will
   be opened to answer. */

/* The site chrome. There is no room for a brand bar above a board, and the
   bottom bar leads out of here. */
body.app-analyzer.review-mode .topbar,
body.app-analyzer.review-mode .site-footer,
body.app-analyzer.review-mode .side-rail,
body.app-analyzer.review-mode .rail-brand,
body.app-analyzer.review-mode .streak-rail { display: none !important; }

/* The app's own Play/Puzzles/Bots/Ranks bar. Those are destinations, and this
   screen is not a destination - it is a thing you are doing. The analyzer's bar
   takes its place at the foot of the screen and leads out to the start screen. */
body.app-analyzer.review-mode #tabbar { display: none !important; }
/* ...and with it the room the page was reserving for it (tabbar.js puts
   `has-tabbar` on the body, which pads the page's foot). */
body.app-analyzer.review-mode.has-tabbar .app,
body.app-analyzer.review-mode.has-tabbar .layout { padding-bottom: 0 !important; }

/* The vertical eval bar beside the board. Replaced by the horizontal one in the
   top row - see section 2. */
body.app-analyzer.review-mode #eval-bar { display: none !important; }
/* The seat cards were being shifted right to stay centred on a board that the
   vertical bar had pushed off-centre. No bar, no offset. */
body.app-analyzer.review-mode .board-wrap .seat-bar { margin-left: 0 !important; }

/* Both move tickers. The strip is a horizontal scroller directly above a row of
   arrows that does the same stepping, and it is the single biggest block of
   height under the board. The arrows moved to the bottom bar; the moves
   themselves are in the docked move list - see section 5. */
body.app-analyzer.review-mode .move-strip,
body.app-analyzer.review-mode .rv-strip { display: none !important; }

/* The old step-arrow row, the web app's Rotate/Place/Cancel bar and the emote
   bar (nobody is here to chat).

   .touch-controls goes because it is a bar of its own under the board, not
   because its controls have no job here - a wall CAN be placed on an analysis
   board, and until now there was no way to rotate or confirm one. Those three
   actions are re-expressed inside the bottom bar, which takes them over
   wholesale for as long as a wall is armed. See .aa-wall in section 6. */
body.app-analyzer.review-mode #review-nav,
body.app-analyzer.review-mode .touch-controls,
body.app-analyzer.review-mode .emote-bar { display: none !important; }

/* The board's own corner flip chip: flip lives in the top row now. */
body.app-analyzer.review-mode .board-flip-btn { display: none !important; }

/* The home screen's furniture. Both of these are gated on `not(.in-game)`
   upstream, and review mode is not a game - so opening the analyzer from
   anywhere the body has not also been marked in-game (a shared game link, a
   profile's game list) drops the signed-out landing card straight onto the
   analysis board and the pitch section below it. They belong to the home
   screen; this screen is not it, whatever else the body happens to say. */
body.app-analyzer.review-mode .board-cta,
body.app-analyzer.review-mode .signup-pitch,
body.app-analyzer.review-mode .home-panel { display: none !important; }

/* --- the review panel ---------------------------------------------------
   It cannot simply be hidden: the Game review REPORT lives inside it, and on a
   phone that report is a full-screen fixed sheet. So the panel is reduced to a
   0x0 anchor that paints nothing and reserves nothing, its card stops being a
   card (display:contents), and the two views it holds are dealt with
   individually - the moves view is MOVED OUT of it (section 5), the report
   sheet stays here and still works.

   Deliberately NOT pointer-events:none: the report sheet is a descendant and
   would inherit it, leaving a visible sheet that ignores every tap. A 0x0 box
   cannot intercept anything anyway. */
body.app-analyzer.review-mode #review-panel {
  position: absolute; width: 0; height: 0;
  padding: 0; margin: 0; border: 0; gap: 0;
  background: none; box-shadow: none; overflow: visible;
}
body.app-analyzer.review-mode #review-panel .play-card { display: contents; }
/* The header row (two tabs + eval + gear + engine switch): its controls are in
   the top row now, and its tabs address views this screen does not show. */
body.app-analyzer.review-mode .rv-tabs,
/* "Back to game" - now the arrow in the screen's top-right corner, and it
   leads out of the analyzer entirely rather than back to the board underneath. */
body.app-analyzer.review-mode .rv-card > .controls { display: none !important; }

/* #rv-moves-view is NOT hidden: analyzer-app.js lifts it out of this panel and
   docks it under the bottom player's row. See section 5. */

/* =======================================================================
   2. The top row: the evaluation, the number, the board's controls, the exit
   =======================================================================
   Above the board AND above the player rows, so the first thing on the screen
   is who is winning and by how much.

   It is TWO stacked pieces, not one line: the bar itself, edge to edge, and a
   line under it carrying the reading at the left and, at the right, the board's
   two controls and the way off this screen.

   It used to be one line - a rounded pill about a third of the screen wide with
   the number inside it and the buttons floating beside it - and that shape cost
   45px of a phone's height to show a quantity that is a POSITION along a line.
   A rule the full width of the screen says the same thing in a fraction of that
   and says it more precisely, because the whole width is the scale.

   The rule itself is now 15px rather than 10, and the line under it sits a few
   pixels lower. Ten pixels was thin enough that the boundary between the two
   colours - the one thing on this row you actually read - was a hairline you
   had to look for, and the row as a whole was crowded up under the status bar.
   Half as much again is still a rule and not a band, and the screen underneath
   it starts a touch further down, which is room the board had to spare. */
.aa-top {
  display: none;                      /* only ever shown in review mode */
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  box-sizing: border-box;
  /* The status bar's inset is carried HERE and nowhere else. .app pads for it
     too by default, and the two together were pushing this whole screen down by
     the height of the notch twice over - which is what put the board low enough
     to need scrolling to reach. Section 4 zeroes .app's copy.
     The +5 is the "everything a tad lower" above: clear of the status bar
     rather than tucked against it. */
  padding-top: calc(env(safe-area-inset-top) + 5px);
}
body.app-analyzer.review-mode .aa-top { display: flex; }

/* The reading and the two controls, on one thin line directly under the bar -
   which puts the controls at the bar's bottom-right corner. In the flow now
   rather than pinned: with the bar spanning the full width there is nothing to
   centre them against and nothing for them to overlap. */
.aa-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  /* Clear of a rounded corner or a landscape notch on either side. */
  padding: 7px max(10px, env(safe-area-inset-right)) 4px max(10px, env(safe-area-inset-left));
}
.aa-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

/* The track is the TOP player's colour; the fill is the BOTTOM player's and
   grows from the left. That is the vertical bar's own convention turned on its
   side (bottom of the board = left of the bar), so a player who has used the
   web analyzer reads it the same way. The colours swap with the board - see
   .is-flipped below and toggleFlip in analysis.js. */
.aa-eval {
  position: relative;
  flex: none;
  /* Side to side. The screen's full width IS the scale, so the boundary between
     the two colours can be read against the board directly underneath it - a
     fill that stops above the e-file means the game is level. A pill inset from
     both edges could never do that, whatever its length. */
  width: 100%;
  /* 15px. See the note at the top of this section for why it is not 10. */
  height: 15px;
  /* Square: it meets both screen edges, and a rounded end at a screen edge
     reads as a component that failed to reach it. */
  border-radius: 0;
  overflow: hidden;
  background: var(--p2);
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .3);
}
.aa-eval-fill {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 50%;
  background: var(--p1);
  /* Same curve and duration as the vertical bar it replaces, so the movement
     reads as the same object. */
  transition: width .4s cubic-bezier(.22, .61, .36, 1);
}
.aa-eval.is-flipped { background: var(--p1); }
.aa-eval.is-flipped .aa-eval-fill { background: var(--p2); }
/* A soft sheen over the fills, matching the vertical bar's finish. */
.aa-eval::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .18) 0%, rgba(255, 255, 255, 0) 55%, rgba(0, 0, 0, .16) 100%);
}

/* The number, at the left end of the line under the bar.
 *
 * It used to ride inside the bar on a dark lozenge, which a 10px rule has no
 * room for. Out here it needs no scrim - it is on the page background, not over
 * two player colours - so it is plain text and reads at a glance. Tabular
 * figures so it does not jitter as it counts. */
.aa-pct {
  flex: none;
  font-size: 15px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -.2px;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-primary);
}
/* Engine off: analysis.js hides the eval bar entirely, and a stale number would
   be a lie. The row keeps its height so the board does not jump. */
.aa-pct.is-idle { color: rgba(255, 255, 255, .5); }

/* Flip and the gear: deliberately quiet. They change how the board is DRAWN,
   which is a thing you do once and then forget, so they take the least room
   that is still hittable and let the reading have the row.
   26px is under Apple's 44pt target, so the tap area is pushed back out to
   44px with a transparent ::before rather than by growing the button - the
   mark stays small, the thing you can hit does not. */
.aa-icon {
  position: relative;
  flex: none;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-surface-raised);
  color: var(--color-text-muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, background .12s ease, color .12s ease;
}
.aa-icon::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}
.aa-icon:active {
  transform: scale(.9);
  background: var(--color-surface);
  color: var(--color-text-primary);
}

/* The way out, at the head of the top row's controls: an arrow, no word.
 *
 * It was a labelled green button in the bottom bar. Up here it is off the
 * thumb's path - the bar below is tapped over and over while a game is read,
 * and one of those taps ending the session was a real outcome.
 *
 * Slightly bigger than flip and the gear, and in the full text colour rather
 * than the muted one, because those two change how the board is DRAWN and this
 * one leaves. The 12px margin is not cosmetic: every .aa-icon carries a 44px
 * transparent tap area, and at the row's 6px gap this one's would have
 * overlapped its neighbour's - so half of "flip the board" would have quit the
 * analyzer. 18px of separation clears both pseudo-elements (7px + 9px). */
.aa-icon.aa-exit {
  width: 30px; height: 30px;
  margin-right: 12px;
  color: var(--color-text-primary);
}

/* =======================================================================
   3. The player rows
   =======================================================================
   One line each, and only what identifies the player and what they have left
   to play with: the pawn colour, the name, the wall count. Everything else the
   seat card can carry is a live-game concern that the analyzer inherited by
   sharing the markup - rating, stake, connection dot, turn flag, clock - and
   every one of them is height the board could have had. */
body.app-analyzer.review-mode .board-wrap.seats-on .seat-bar {
  padding: 3px 12px;
  gap: 8px;
  min-height: 0;
  border-radius: 10px;
  background: transparent;
  border: 0;
  box-shadow: none;
}
body.app-analyzer.review-mode .seat-bar .seat-avatar,
body.app-analyzer.review-mode .seat-bar .seat-elo,
body.app-analyzer.review-mode .seat-bar .seat-stake,
body.app-analyzer.review-mode .seat-bar .conn-dot,
body.app-analyzer.review-mode .seat-bar .player-time { display: none !important; }

/* Name and walls on ONE line (the phone layout stacks them). */
body.app-analyzer.review-mode .board-wrap.seats-on .seat-bar .seat-main {
  display: flex; flex-direction: row; align-items: center;
  gap: 8px; width: 100%; min-width: 0;
}
body.app-analyzer.review-mode .board-wrap.seats-on .seat-bar .seat-line {
  gap: 7px; min-width: 0; flex: 1 1 auto;
}
body.app-analyzer.review-mode .board-wrap.seats-on .seat-bar .player-name {
  font-size: 13px; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The wall pips go; the number stays. Ten little fences read as decoration at
   this size - the count is the fact, and it is the fact a player checks. The
   fence glyph beside it says what the number counts, which the pips were doing
   before. */
body.app-analyzer.review-mode .seat-bar .walls-row { display: none !important; }
body.app-analyzer.review-mode .board-wrap.seats-on .seat-bar .bar-walls {
  margin-left: auto; flex: none; gap: 5px;
  display: inline-flex; align-items: center;
}
/* The fence rides INSIDE the count rather than beside it, so it inherits the
   count's colour instead of picking its own. The site already colours these
   per seat (`#count-1` is gold), and a mark in a second colour beside the
   number it labels reads as two separate facts. */
body.app-analyzer.review-mode .seat-bar .walls-count {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 13px; font-weight: 800;
  font-variant-numeric: tabular-nums;
}
/* The fence mark, drawn in CSS so it costs no markup and no request: two short
   uprights under a rail, in whatever colour the count is wearing. */
body.app-analyzer.review-mode .seat-bar .walls-count::after {
  content: '';
  width: 9px; height: 11px;
  flex: none;
  opacity: .72;
  background: currentColor;
  -webkit-mask: linear-gradient(#000 0 0) 0 0/9px 3px no-repeat,
                linear-gradient(#000 0 0) 1px 4px/2px 7px no-repeat,
                linear-gradient(#000 0 0) 6px 4px/2px 7px no-repeat;
          mask: linear-gradient(#000 0 0) 0 0/9px 3px no-repeat,
                linear-gradient(#000 0 0) 1px 4px/2px 7px no-repeat,
                linear-gradient(#000 0 0) 6px 4px/2px 7px no-repeat;
}

/* =======================================================================
   4. The board
   =======================================================================
   Everything above exists to give the board the rest of the screen. It runs
   edge to edge and is capped only by what the two bars and the two seat rows
   actually occupy - measured, not guessed, since the safe-area inset differs
   between an iPhone with a notch and one without.

   The top row is a child of .board-wrap (order 0, ahead of the top seat card)
   so it is part of the same column and needs no separate positioning; the
   bottom bar is fixed to the foot of the screen and so is subtracted here. */
/* --- the screen does not scroll -----------------------------------------
   Not "does not usually need to" - CANNOT. A board you have to scroll to is not
   a board you can read a game off, the step arrows are tapped over and over and
   the page must not move under them, and the move list docked at the foot has a
   scroller of its own that a page scroll would fight for the same gesture.

   `overflow: hidden` on BODY, not on <html>. With <html> at `visible` the value
   is propagated from body up to the VIEWPORT, and body itself computes back to
   `visible` - so nothing here becomes a scroll container. That distinction is
   the whole point: a real scroll container on the root takes `position: fixed`
   children out of the scrolling tree, which is what once stranded the app's tab
   bar mid-gesture (see the html/body note in styles.css). Propagated, the
   viewport simply has nothing to scroll and the fixed bottom bar is untouched.

   Everything below exists so that nothing is ever OUTSIDE that locked viewport:
   the column is packed from the top, and the one part that can want more room
   than it has - the move list - absorbs the slack and scrolls inside itself. */
body.app-analyzer.review-mode { overflow: hidden; }

body.app-analyzer.review-mode .layout {
  padding: 0;
  align-content: start;
}
/* .app pads its top for the status bar by default, and .aa-top now pads for it
   too. Both were applying, so this screen started one full notch lower than it
   should have - about 47px of nothing above the eval bar, and the board pushed
   down far enough that the bottom of the screen ran past the viewport and the
   page scrolled to reach it. The inset is charged ONCE, on .aa-top. */
body.app-analyzer.review-mode .app {
  min-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}
body.app-analyzer.review-mode .board-wrap {
  gap: 4px;
  --strip-h: 0px;
  --bar-h: 0px;
  /* The height the move dock is guaranteed before the board is allowed to take
     any more. Roughly four move rows and the caption under them - enough to be
     a move list rather than a hint that one exists. */
  --aa-moves-min: 104px;
  /* Exactly the screen, less the fixed bar at its foot. Fixed, not a minimum:
     the column has to be a known box for the dock at the end of it to divide up
     what is left, and nothing may extend past it now that the viewport is
     locked. */
  height: calc(100dvh - var(--aa-tab-h));
  min-height: 0;
  /* A square board on a phone is capped by the WIDTH almost always (390px wide
     against ~660px of usable height), so the height term is the guard for
     landscape and for the short screens, not the usual binding constraint.

     No env() terms here: --aa-top-h and --aa-tab-h are MEASURED off the real
     elements, and each already includes the safe-area padding it carries.
     Subtracting the insets again on top of them was the second half of the
     double-count above. */
  --board-size: min(
      100vw,
      600px,
      calc(100dvh - var(--aa-top-h) - var(--aa-tab-h) - var(--aa-seats-h)
           - var(--aa-moves-min))
  );
}
/* The top row is measured to its own content, not stretched to the board. */
body.app-analyzer.review-mode .board-wrap > .aa-top { order: 0; }

/* --- where the leftover height goes -------------------------------------
   The board is square and the screen is not, so on a phone there are always
   ~150px spare. All of it now goes to the move list at the foot of the column,
   and the column is packed from the TOP - so the eval bar sits under the status
   bar, the board sits under that, and the slack collects in the one place that
   can use it. Nothing floats in the middle of the screen and nothing moves when
   the list gets longer.

   It was centred before, with the whole block drifting down the screen as the
   slack split above and below it. That is what made this screen read as "too
   far down", and it is why it was worth scrolling in the first place. */
body.app-analyzer.review-mode .board-wrap { justify-content: flex-start; }
body.app-analyzer.review-mode .board-row { gap: 0; flex: none; }
body.app-analyzer.review-mode .board,
body.app-analyzer.review-mode .board-frame { border-radius: 0; }

/* =======================================================================
   5. The move list, docked under the bottom player
   =======================================================================
   Between the bottom seat card and the bar at the foot of the screen: under the
   name of the player whose moves you are most likely reading, and above the
   thumb's own row. It is the web analyzer's own moves view, lifted out of the
   side panel by analyzer-app.js - not a copy - so the numbered main line, the
   inline variations, their promote/delete controls and the caption are all the
   ones analysis.js already builds and already listens to.

   The app used to hide it and step through the game with a ticker instead. The
   ticker is gone (section 1) and the arrows moved to the bottom bar, which left
   the screen with no way to SEE the game - only to walk it one move at a time -
   and no way to see a variation at all, which made exploring one a thing you
   could do but not read.

   It is the last row of .board-wrap's column and the only one that flexes, so
   every pixel the square board cannot use ends up here. */
.aa-moves { display: none; }
body.app-analyzer.review-mode .aa-moves {
  display: flex;
  order: 4;                 /* after #card-1 (3), before the hidden in-game rows */
  width: 100%;
  min-height: 0;
  /* Grows into the slack, shrinks below the reserve on a short screen rather
     than pushing the bar off the bottom of a viewport that cannot scroll. */
  flex: 1 1 var(--aa-moves-min);
}
body.app-analyzer.review-mode .aa-moves > .rv-view {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
/* showView('report') still hides it the ordinary way when the report sheet is
   up - the sheet covers the screen, so the gap it leaves is never seen. */
body.app-analyzer.review-mode .aa-moves > .rv-view.hidden { display: none; }

/* The box loses its card edges: it is the foot of the screen, not an object
   sitting on it, so only the line separating it from the seat row above. */
body.app-analyzer.review-mode .aa-moves .history-box {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  width: 100%;
  border: 0;
  border-top: 1px solid var(--color-border);
  border-radius: 0;
  background: var(--color-surface);
}

/* The phone stylesheet hides this list outright (`body.review-mode .rv-view
   .review-list`), because on the site's phone layout the ticker under the board
   carries the moves. There is no ticker here. Restated at a higher specificity
   rather than edited upstream, so the website's phone analyzer is untouched. */
body.app-analyzer.review-mode #rv-moves-view .review-list {
  display: block;
  flex: 1 1 0;
  min-height: 0;
  max-height: none;
  overflow-y: auto;
  /* Momentum, and the gesture stays in the list: the page behind it cannot
     scroll at all now, so without this a flick past the end of the list would
     simply stop dead. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 4px 8px;
  padding-left: max(8px, env(safe-area-inset-left));
  padding-right: max(8px, env(safe-area-inset-right));
}
body.app-analyzer.review-mode .aa-moves .review-move {
  padding: 5px 8px;
  font-size: 13.5px;
  border-radius: 6px;
}
body.app-analyzer.review-mode .aa-moves .rm-num { width: 26px; }
body.app-analyzer.review-mode .aa-moves .rm-chip { width: 12px; height: 12px; }
/* Where you are: a green wash with an accent edge down the left. The panel
   already styles the selection this way, but that rule is `.rv-card
   .review-move.active` and the list is no longer inside the card - so without
   this it fell back to the site's outlined pill, which in a dark dock reads as
   a text field rather than as the row you are standing on. */
body.app-analyzer.review-mode .aa-moves .review-move.active {
  background: rgba(90, 158, 63, .22);
  outline: 0;
  box-shadow: inset 2px 0 0 var(--color-accent);
}

/* --- the foot: the way back to the main line, and the caption ------------
   Both matter more here than they do on the site, because this is the only
   place a variation announces itself: the caption is what says "Variation", and
   the button is the way off one. Compact, one row, and pinned under the scroll
   so neither moves as the list fills. */
body.app-analyzer.review-mode .aa-moves .rv-moves-foot {
  flex: none;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  padding-left: max(10px, env(safe-area-inset-left));
  padding-right: max(10px, env(safe-area-inset-right));
}
body.app-analyzer.review-mode .aa-moves .rv-moves-foot .review-caption {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.app-analyzer.review-mode .aa-moves .rv-moves-foot .btn.small {
  align-self: center;
  flex: none;
  order: 2;                 /* the action sits at the right, the caption reads first */
  min-height: 30px;
  padding: 0 11px;
  font-size: 12px;
}
/* The solver's verdict is a full-width block in the side panel and would double
   the height of this dock. The caption beside it already carries what the
   position is; the verdict is a desktop luxury. */
body.app-analyzer.review-mode .aa-moves .review-verdict { display: none !important; }

/* --- variations ---------------------------------------------------------
   The two controls at the head of every variation line - promote and delete -
   are 12px glyphs with 3px of padding on the site. That is an 18px target, well
   under half of Apple's 44pt, and the two sit shoulder to shoulder: on a phone
   the realistic outcome of reaching for "promote this line" is deleting it, and
   deletion has no undo here.

   So both grow to a 30px target, they are separated, and delete is pushed to
   the far side of the pair with the gap between them. Delete also stops being
   the same grey as promote - a control that destroys work should not be
   indistinguishable from the one beside it. */
body.app-analyzer.review-mode .aa-moves .review-var-line {
  gap: 3px 5px;
  padding: 3px 6px;
  font-size: 13px;
}
body.app-analyzer.review-mode .aa-moves .var-act {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  font-size: 14px;
  opacity: .8;
  -webkit-tap-highlight-color: transparent;
}
/* The SECOND of the pair is delete. Not `:last-of-type` - the line's last
   <button> is the variation's last move, not this. */
body.app-analyzer.review-mode .aa-moves .var-act + .var-act {
  margin-right: 6px;
  color: #d67a6a;
}
body.app-analyzer.review-mode .aa-moves .var-act:active {
  background: var(--color-surface-raised);
  opacity: 1;
}
/* The moves themselves take a real target too - they are the primary thing in
   a variation line, and they were 12.5px text with 1px of padding. */
body.app-analyzer.review-mode .aa-moves .rv-move {
  padding: 5px 7px;
  font-size: 13px;
}

/* =======================================================================
   6. The bottom bar: every action on this screen
   =======================================================================
   Fixed to the foot, wearing the same surface, border and safe-area padding as
   the app's own tab bar, because it stands in that bar's place - a player's
   thumb should find the same thing in the same spot.

   Two groups now: the step arrows, which take the whole left of the row, and
   Game review at a fixed width on the right. The way out left this bar for the
   screen's top-right corner (section 2), and the arrows spent every pixel of
   what it gave back - they are the control this screen exists to be tapped
   with, and at 34px they were the smallest thing in the row rather than the
   largest.

   While a wall is armed the bar carries neither group: it becomes Rotate /
   Place / Cancel and nothing else. See .aa-wall at the foot of this section. */
.aa-tabbar {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  padding-bottom: calc(7px + env(safe-area-inset-bottom));
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  box-shadow: 0 -6px 18px rgba(0, 0, 0, .22);
}
body.app-analyzer.review-mode .aa-tabbar { display: flex; }

/* The budget, inverted. It used to be Game review that took the slack and the
   arrows that took the least width still worth tapping; now Game review is a
   fixed 104px and the arrows divide everything else. Four fixed widths became
   four flex ratios for the same reason: with the row's contents no longer
   fixed, the arrows should be as large as the screen in front of them allows -
   a 430px phone gets bigger ones than a 360px phone, rather than both getting
   the width the smallest one could afford.

   On a 390px screen that is ~51px for first/last and ~69px for prev/next,
   against 34 and 40 before. On a 360px screen, ~45 and ~60 - still larger than
   the widest button this row used to have. */
.aa-steps { display: flex; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; }
.aa-step {
  flex: 1 1 0;
  min-width: 0;
  height: 50px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 11px;
  background: var(--color-surface-raised);
  color: var(--color-text-primary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, opacity .12s ease;
}
.aa-step:active:not(:disabled) { transform: scale(.9); }
/* Faded, not removed: the row must not change width at the ends of the game or
   the arrows move under the thumb that is repeatedly tapping them. */
.aa-step:disabled { opacity: .3; cursor: default; }
/* Prev/next are the two that get used move after move, so they are the wide
   ones; first/last are occasional. A ratio rather than a width, so the
   difference between them holds at every screen size. */
.aa-step.is-major { flex-grow: 1.35; }

/* Game review: the app's primary green, and the one coloured thing left in the
   row now that the way out has gone up to the corner.
 *
 * Same green and same white as .btn.primary everywhere else in the app, so it
 * reads as the button it is rather than as a fifth control that happens to be
 * tinted. The star stays gold on it - gold on green is the pairing the report
 * itself uses for a grade, and it is the one mark in this bar that is a THING
 * rather than a direction.
 *
 * Fixed width rather than flexing: what it says is one short word, and every
 * pixel it took beyond that was a pixel off an arrow. It still doubles as its
 * own progress bar, which is why the fill is a child and not a background. */
.aa-review {
  position: relative;
  flex: 0 0 104px;
  min-width: 0;
  height: 50px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: var(--color-accent);
  color: #fff;
  font-size: 13px; font-weight: 800;
  box-shadow: 0 2px 8px rgba(90, 158, 63, .25);
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, filter .12s ease;
}
.aa-review:active { transform: scale(.96); filter: brightness(.92); }
/* White rather than the old gold wash: the button under it is green now, and a
   gold fill on green read as a second colour arriving rather than as the same
   button filling up. */
.aa-review-fill {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 0%;
  background: rgba(255, 255, 255, .26);
  transition: width .25s linear;
  pointer-events: none;
}
.aa-review-star, .aa-review-label { position: relative; }
.aa-review-star { color: #f0c674; font-size: 14px; text-shadow: 0 1px 1px rgba(0, 0, 0, .3); }
.aa-review-label {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Once the game HAS been reviewed the button is the way back to the report
   rather than a thing to start, so it lightens by one step instead of shouting
   the same shade at a player who has already used it. */
.aa-review.is-graded { background: var(--color-accent-hover); }
.aa-review.is-running { pointer-events: none; }
/* The star steps out while the review runs. It is the mark for "this game has
   a report", and during the run there is no report yet - meanwhile the fill
   sweeping across the button already says what is happening, and the twenty
   pixels the star was holding are what let the ply counter fit beside it on a
   360px screen. */
.aa-review.is-running .aa-review-star { display: none; }
.aa-review.hidden { display: none; }

/* --- placing a wall: the bar becomes three buttons ----------------------
   A wall on an analysis board is armed by a tap and then needs rotating and
   confirming, and this screen hides the bar the web app puts those on. So for
   as long as one is pending the bottom bar IS that bar: Rotate, Place, Cancel,
   filling the row equally, and nothing else.

   Everything else goes rather than shuffling aside because everything else is
   destructive here - stepping to another move throws the pending wall away, and
   Game review is a minute-long job. The three that remain are the whole of what
   the player can be doing, and each gets a third of the screen to be tapped in.

   Driven by `is-placing` on the bar, which analyzer-app.js toggles off the
   armed preview on the board itself (see syncWall there). */
.aa-wall { display: none; }
.aa-tabbar.is-placing .aa-steps,
.aa-tabbar.is-placing .aa-review { display: none; }
.aa-tabbar.is-placing .aa-wall {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
}
.aa-wallbtn {
  flex: 1 1 0;
  min-width: 0;
  height: 50px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  padding: 0 6px;
  border: 1px solid var(--color-border);
  border-radius: 11px;
  background: var(--color-surface-raised);
  color: var(--color-text-primary);
  font-size: 13.5px; font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, filter .12s ease;
}
.aa-wallbtn .aa-wall-label { white-space: nowrap; }
.aa-wallbtn:active:not(:disabled) { transform: scale(.96); filter: brightness(1.15); }
/* Place is the commit, in the same green as every other commit in the app. */
.aa-wallbtn.is-place {
  background: var(--color-accent);
  border-color: transparent;
  color: #fff;
  font-weight: 800;
}
/* An illegal slot stays armed - the player is still aiming - so Place fades
   rather than the row losing a button from under the thumb mid-aim. */
.aa-wallbtn.is-place:disabled { opacity: .38; cursor: default; }
/* Cancel discards work, so it says so in the app's danger colour - the mark
   and the word together, since the icon paints from currentColor.
 *
 * The BOX stays the same grey as Rotate. It was drawn in the muted text colour
 * at a lighter weight, on the theory that a destructive control should not
 * shout; what that actually produced was grey text on a grey button, which is
 * what every disabled control on this screen looks like - so the one button
 * whose whole job is to be available read as the one you could not press.
 * Red on the same surface says "this one undoes things" without saying "this
 * one is off", and it keeps Place as the only filled button in the row. */
.aa-wallbtn.is-cancel { color: var(--color-danger, #d8564a); }

/* The narrowest phones. Game review gives up eight pixels rather than the
   arrows giving up any: "Review" still fits at 96px, and the arrows are the
   thing this screen is tapped with. */
@media (max-width: 360px) {
  .aa-review { flex-basis: 96px; }
  .aa-wallbtn { font-size: 13px; gap: 5px; }
}

/* =======================================================================
   7. The Game review sheet
   =======================================================================
   Left working exactly as the web app builds it - a full-screen fixed sheet
   with its own title and close button - because that is already the right
   shape here. It only needs to clear the bottom bar, which is fixed above it.

   It keeps its own scroller (position:fixed, overflow-y:auto) and is unaffected
   by the locked viewport in section 4 - which is the reason that lock is body's
   and not <html>'s: a real scroll container on the root would have taken this
   sheet, and the bar under it, out of the scrolling tree. */
body.app-analyzer.review-mode #rv-report-view:not(.hidden) {
  z-index: 90;
  /* --aa-tab-h is measured off the bar, and the bar already pads for the home
     indicator - so the inset is NOT added again here. */
  padding-bottom: var(--aa-tab-h);
}

/* Reduced motion: the bar still moves (its position IS the information) but
   nothing springs or scales. */
@media (prefers-reduced-motion: reduce) {
  .aa-eval-fill, .aa-review-fill { transition: none; }
  .aa-icon:active, .aa-step:active:not(:disabled),
  .aa-review:active, .aa-wallbtn:active:not(:disabled) { transform: none; }
}


/* ===========================================================================
   12. THE LEARNING BOARD IN THE APP  (body.learn-mode)
   ===========================================================================
   The learning board is the analyzer with free edit on (js/analysis.js), so it
   arrives here wearing this whole skin already - the dock, the top bar, the
   bottom bar - and needs none of it changed. One thing does not survive the
   trip: its free-edit toolbar is a child of .rv-moves-foot, and section 8 turns
   that foot into a SINGLE ROW built for a caption plus one small button.

   Three items in that row do not fit 390px. Measured on an iPhone 14 viewport,
   the toolbar wrapped - Clear dropped onto a second line under the White/Blue
   segment - and the caption beside it truncated to "Drag a pawn or a wall
   any...", which is the half of the sentence that says nothing.

   So in this mode, and only in this mode, the foot goes back to a column: the
   sentence gets the full width above, the controls get the full width below,
   and nothing wraps or ellipsises. It costs one row of height in a dock that
   has spare - on a learning board the move list is short by definition. */
body.app-analyzer.review-mode.learn-mode .aa-moves .rv-moves-foot {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
}
/* The sentence reads first and the controls act under it - the reverse of the
   played-game foot, where the caption is a label on a board you are watching
   and the action beside it is the point. */
body.app-analyzer.review-mode.learn-mode .aa-moves .rv-moves-foot .review-caption {
  order: -1;
  flex: none;
}
body.app-analyzer.review-mode.learn-mode .aa-moves .learn-bar {
  width: 100%;
  flex-wrap: nowrap;
}
/* Section 8 pushes .btn.small to the right end of the row; here the row is the
   toolbar, and Clear is already held apart from the pair by its own margin. */
body.app-analyzer.review-mode.learn-mode .aa-moves .rv-moves-foot .btn.small {
  order: 0;
}
