/* WHERE A WINDOW SITS, AND NOTHING ELSE.
   This is the placement half of the old public/panels.css, carried across so the HUD design
   system can own everything a window LOOKS like. .win is position:relative and the design
   system positions nothing at all, so without this sheet every one of the game's windows lands
   in the document flow, stacked down the top of <body>.

   What this file deliberately does not own: any face, rim, shadow, border, colour, font,
   padding or gap inside a window; any component; any state. If a rule here says how something
   is drawn rather than where it is, it is in the wrong file - it belongs in
   docs/hud-ui-design.html, which is re-extracted to public/hud-ui/design.css.

   It reads no --gs-* token from any other sheet. The old sheet read 52 of them and would have
   collapsed silently the day play.html stopped loading gs.css: a token that resolves to nothing
   inside a `width` takes the whole declaration with it. Every one is a literal here, or a token
   this file declares itself. The z-index literals are the shared Z table (window 20, dialog 80,
   card 90, scrim 100, menu 120); --gs-hud-scale keeps its old name because src/client/ui.ts
   reads and writes that exact property on <html>, but its only declaration is now the one below.

   Loaded LAST, after design.css. Dimensions are in em against the 15px window size, so a
   placement scales with the window it places. */

/* The always-on HUD is NOT placed here. public/hud.css owns every .hud-* rule, its offsets
   included, because the world layer is its own language and not this design system's: no box,
   no plate, no well anywhere on screen while you play. A parchment window is the right answer
   for a thing you open and close and the wrong answer for a thing that is always there covering
   the game. This sheet places WINDOWS. A HUD cluster offset appearing below is a bug. */

/* The HUD scale. setHudScale in src/client/ui.ts writes this onto <html> and reads it back, so
   the default has to be a real declaration somewhere and this sheet is the last one standing. */
:root { --gs-hud-scale: 1.15; }
/* The height of the hotbar, which is what the conversation stands on: 34px of slot (hud.css
   --hud-slot) at the HUD's own 1.15 zoom, rounded up to a whole pixel. */
:root { --hud-bar-h: 40px; }

/* ------------------------------------------------------------------ GLOBAL BEHAVIOUR */
/* Three things the old Stonebevel sheet was quietly carrying for the whole document, none of
   which the design system restates. A drag is this game's main gesture, and without the first
   rule every drag across a panel paints a text selection instead. The second keeps native
   scrollbars and the surviving form controls dark on a machine set to a light theme. The third
   is the colour behind the renderer canvas, which is what a player sees while the map streams.
   Box-sizing is NOT here: design.css already sets border-box on `*`. */
* { user-select: none; -webkit-user-select: none; }
input, textarea, [contenteditable] { user-select: text; -webkit-user-select: text; }
html { color-scheme: dark; }
html, body { background: #2c2c2f; }

/* The page frame. src/client/ui.ts used to write these three declarations into a <style> tag at
   mount, which put a panel file in charge of the document and read --gs-void out of the sheet
   that is being retired - and a token that resolves to nothing takes its whole declaration with
   it. The colour is the one above; only the frame is left to say. */
body { margin: 0; overflow: hidden; }
canvas { display: block; }

/* The world's announcement ladder: centred under the top edge, lines stacking downward, and
   never a click target - it prints over the world while the player is still walking through it.
   Above the window stack and below the scrim, so a modal covers it. */
.tl-cast {
  position: fixed;
  left: 50%;
  top: 5.6em;
  transform: translateX(-50%);
  z-index: 94;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .333em;
  width: max-content;
  max-width: min(41.333em, calc(100vw - 2.133em));
  pointer-events: none;
}

/* The proclamation, across the upper third: the one band the world says something in. Edge to
   edge, because the wash has no edge of its own and a band inset from the sides would read as a
   strip laid over the game. Never a click target - the player is still walking under it - and one
   layer above the ladder, because your own arrival outranks somebody else's good luck, and under
   the scrim, so a question you have to answer still covers it. */
.tl-procl {
  position: fixed;
  left: 0;
  right: 0;
  top: 22%;
  z-index: 95;
  pointer-events: none;
}

/* A PHONE shrinks both by its own height, the factor every window already takes (orientation.ts
   writes --win-k = innerHeight / 640, never above 1). Both sit straight on body at the page's 15px,
   so on a 400px-tall screen a broadcast and your own notice were desktop-sized over half the world.
   Floored at 11px so a line stays readable; the big broadcast stays 1.4x of it (design.css). Your
   own notice also sets tighter: 2.1em with wide tracking ran a long sentence edge to edge. */
@media (hover: none) and (pointer: coarse) {
  .tl-cast, .tl-procl { font-size: max(11px, calc(15px * var(--win-k, 1))); }
  .tl-cast { top: 4.2em; }
  /* Four broadcast lines reach a third of a 400px screen, so your own notice stands below them
     rather than printing over the ladder. */
  .tl-procl { top: 34%; }
  .tl-procl .procl__text { font-size: 1.6em; letter-spacing: .08em; }
}

/* The host for a card that follows the pointer: a hover card, a dragged item's ghost. Above every
   window, below the scrim, and never a hit target of its own. */
.card--float { position: fixed; z-index: 90; pointer-events: none; }

/* The NPC conversation stands on the hotbar, centred, and it is wide: the quest it offers is
   drawn INSIDE it now (src/client/quest.ts), so the body carries a portrait, a speech well and a
   sheet side by side. Centred in the screen it covered the body it was talking to; over the bar it
   covers the floor nobody is looking at. --hud-bar-h is the hotbar's own height, declared here
   because this sheet reads no token it does not own - see the file header.

   The NPC conversation and the small service window it opens. Neither is in the window stack -
   the conversation closes itself and takes the service box with it - so nobody else places them,
   and they were the last two windows placing themselves from a <style> tag src/client/dialog-panel.ts
   wrote at mount. The two layers are relative to each other and to the stack (window 20, dialog
   80): the conversation sits just under an ordinary window, and a question raised over the
   service box comes up over it rather than behind it. */
.dlg { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 44em; z-index: 19; }
.dlg-svc { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 23em; z-index: 81; }

/* ------------------------------------------------------------------ THE WINDOWS */
/* One line each: where it opens and how wide. The widths are not decoration, they are the shape
   of the content - a ten-column bag, a doll frame, a bracket. Where the design page draws the
   window, its width IS the width: docs/hud-ui-design.html authors one on the demo, and that is
   the number here. The rest still carry the measurement panels.css made against Stonebevel, and
   the wave that ports each of those re-measures it.

   That distinction is not pedantry. The old widths were taken against a 44px slot and a denser
   face; a `.tile` is 3.33em, so a ten-column bag needs 36.933em of grid plus 2.8em of padding and
   the old 39.333em put the last two columns outside the frame - `.win` has no overflow clip, so
   they hang in the air over the world. A window that is too narrow for the design system's own
   components is a visible break, not a tight fit, and one that is too WIDE leaves the grid
   floating in a band of dead paper, which is the same break read the other way. Both are why
   these two widths are re-derived whenever --tile moves.

   Two of these are DERIVED, not authored: `.warehouse` and `.ps` sit beside the window they trade
   with, so their `right` is that window's width plus two gutters. Change a bag width and its
   neighbour's offset moves with it.

   `max-width` keeps a window inside a laptop viewport; the mobile block at the bottom takes over
   from all of it on a phone. */

/* Bag and warehouse, right-hand side, side by side so an item drags straight across. */
/* 46em, not the 41.333em the grid alone asks for: the foot is what sets this window's width now
   that it carries the Magic Bag switch beside Split, and its natural width measures 535px against
   a grid that needs 478. A window sized to its grid would clip its own buttons. */
.bag { position: fixed; right: .933em; top: 12%; width: 46em; }
/* Magic Bag on: the window holds two pages instead of one, so it is twice the grid plus one
   chrome - 956px, and the em figure is that over --win-size. It stays on the same right edge
   rather than moving, so turning the switch on does not also move the bag out from under the hand.
   The warehouse cannot sit beside a window this wide: with both open they overlap, and that is the
   cost of the spread rather than a bug in it. */
.bag--magic { width: 86.833em; max-width: calc(100vw - 1.867em); }
/* Shut, the bag is one page on the device's cover. It keeps the same right edge as it narrows,
   because the half that stays put is the right one - the cover swings in from the left. */
.bag--folded { width: 43.417em; }
.warehouse { position: fixed; right: 47.867em; top: 12%; width: 46em; }

/* The character sheet, left, clear of the bag. The ceiling is the viewport minus the 8% it opens
   below and a gutter under it: the doll, the equipment columns, the attribute rows and the combat
   table together stand about 51em, which fits a 900px screen and runs off the bottom of the 813px
   one a laptop actually has. See THE VIEWPORT CEILING below for why the body is the scroller. */
.cs {
  position: fixed; left: .933em; top: 8%;
  width: 56em; max-width: calc(100vw - 1.867em);
  max-height: calc(92dvh - 1.6em);
}

/* Everything an NPC raises opens in the viewport centre. */
.ws { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 58em; max-width: calc(100vw - 1.867em); }
.ql { position: fixed; left: 50%; top: 8%; transform: translateX(-50%); width: 60em; max-width: calc(100vw - 1.867em); }

/* The pet roster and its detail sheet, in the same band as the bag they trade with. The sheet
   and the pen both grow with the player's pets - the sheet's Stats page runs to a combat table,
   and the pen draws nine travelling rows plus eight penned ones in one list - so both carry a
   ceiling. The pen is centred, so its ceiling is the whole viewport less a gutter each side;
   the sheet opens at 14%, so its ceiling is what is left under that. */
.pt { position: fixed; right: .933em; top: 14%; width: 46em; }
.ps {
  position: fixed; right: 47.867em; top: 14%;
  width: 50em; max-width: calc(100vw - 1.867em);
  max-height: calc(86dvh - 1.6em);
}
/* The pen's own window: one collection now, the travelling list being the HUD's Pet Slot window,
   which opens beside it at the right edge. Narrow enough that the two do not overlap on a 1366px
   screen. --win-size is declared beside .pd below. */
.pr {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 34em; max-width: calc(100vw - 1.867em);
  max-height: calc(100dvh - 3.2em);
}

/* THE VIEWPORT CEILING, AND WHAT SCROLLS UNDER IT.
   A `.win` is a flex column of head, body and foot, and only the body is `flex: 1`. Capping the
   window alone would therefore squeeze the body's own height while its content kept its size and
   hung out of the frame - the design system sets no overflow on `.win__body`, because most
   windows are measured to fit. So the three that are not say it here, once: the body is the
   scroller, which keeps the title and the buttons in the frame while the list moves.

   `min-height: 0` is not optional. A flex child's default minimum is its content, so without it
   the body refuses to shrink below its rows and the cap above does nothing at all. */
.cs > .win__body, .ps > .win__body, .pr > .win__body { min-height: 0; overflow-y: auto; }

/* THE MARKETPLACE IS THE OTHER HALF OF THAT SAME RULE, WITHOUT THE SCROLLER. Its body is a grid
   that hands its slack to whichever pane is showing, and the pane hands it down to the two lists,
   which scroll inside themselves - so the window must be allowed to CAP the body, and nothing
   here may scroll the body itself or the tab strip and the coin footer would leave the frame.
   `min-height: 0` alone, and it is not optional: a flex item's automatic minimum is its content,
   so a body holding a four-hundred-row list refuses to shrink to the height the window was
   measured to and the whole window grows down the page instead. */
.market > .win__body { min-height: 0; }

/* THE PET BENCH READS AT ITS OWN SIZE. Every window in the game is laid out in em against
   --win-size, which is 12px because a window sits over a live canvas and has to leave the world
   visible. These two are the exception the rule was always going to grow: the dealer's fusion tab
   is a DIAGRAM with five pet tiles, five names, five dropdowns and a rules panel beside it, and
   the pen is two collections side by side - at 12px the names were the smallest text in the
   product and the panel was 552px for content the v0.10 panel gave 760px.
   Redefining the token rather than setting font-size scales the whole panel uniformly: everything
   inside is already in em, so the tiles, the gaps and the type all move together. */
.pd, .pr { --win-size: 14px; }

/* The dealer, one width for three of its tabs. */
.pd { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 50em; max-width: calc(100vw - 1.867em); }
/* And wider for the fourth. The fusion diagram is 22em of its own before the rules, the rate and
   the stone get a column - the same split the v0.10 panel made when it took 760px for fusion and
   660px for everything else. The class is set by the panel when the tab changes, not by a
   selector: the tab lives in the body and a stylesheet cannot see which one is up. */
.pd.pd--fusion { width: 66em; }

/* The growth table scrolls inside itself rather than growing past the viewport. */
.pd-growth {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 34em; max-width: calc(100vw - 1.6em); max-height: calc(100dvh - 1.6em); overflow: auto;
}

/* Trade and commerce. */
.shop { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 38em; max-width: calc(100vw - 1.867em); }
/* The marketplace is the one window with a fixed height: its three panes each fill what is left
   of it and clip inside, so the shell has to be measured rather than grown by its content. */
/* 14px rather than the 12px every other window reads at: this is the densest window in the game,
   and at 12px its prices and item names were the hardest text in it to read. */
.market {
  --win-size: 14px;
  position: fixed; left: 50%; top: 3%; transform: translateX(-50%);
  width: 70em; max-width: calc(100vw - 1.867em);
  height: min(55.333em, calc(100vh - 1.867em)); max-height: min(55.333em, calc(100vh - 1.867em));
}
.gth { position: fixed; left: 50%; top: 12%; transform: translateX(-50%); width: 30em; max-width: calc(100vw - 1.867em); }
.cmp { position: fixed; left: 50%; top: 14%; transform: translateX(-50%); width: 30em; max-width: calc(100vw - 1.867em); }
.ml { position: fixed; left: 50%; top: 6%; transform: translateX(-50%); width: 44em; max-width: calc(100vw - 1.867em); }
.auc { position: fixed; left: 50%; top: 9%; transform: translateX(-50%); width: 50em; max-width: calc(100vw - 1.867em); }
.stall { position: fixed; left: 50%; top: 9%; transform: translateX(-50%); width: 44em; max-width: calc(100vw - 1.867em); }
.trd { position: fixed; left: 50%; top: 10%; transform: translateX(-50%); width: 46em; max-width: calc(100vw - 1.867em); }
.ie { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 42em; max-width: calc(100vw - 1.867em); }
/* The door's four facts sit beside a plate in half the window's width, and one of them is a boss
   name. A kv row is a label and a value pushed to opposite ends of a flex line, and a flex line
   does not wrap: the pair was wider than the column, so the column was wider than the window and
   the values were painted outside the frame. Wrapping lets the value fall under its own label
   instead, which is the one way out of this that keeps both words whole. */
.ie .kv > div { flex-wrap: wrap; }
/* And the reason the whole body hung outside the frame, facts included: a grid column is as wide
   as its widest item's MIN-content, and one item here is a six-tab strip of nowrap tabs with a 7em
   floor each - some 60em of unbreakable row. The window was 34em; everything in it was painted to
   the strip's width. The column is pinned to the window now, and the strip wraps to a second line
   instead of pushing. */
.ie .win__body { grid-template-columns: minmax(0, 1fr); }
.ie .tabs--block { flex-wrap: wrap; }
.ie .tabs--text .tabs__tab { min-width: 5.6em; padding: 0 1.1em; }
/* The reward list reads as a shelf, not a scroll: two goods a line, so a materials path of twenty
   entries is half as tall and the window stops running off the screen. Every path's list, not just
   this one. The hairline is dropped from the last PAIR, since two rows now end the list. */
.ie [data-loot] .rows { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: .9em; }
.ie [data-loot] .rows > .item-row:nth-last-child(-n+2):nth-child(odd),
.ie [data-loot] .rows > .item-row:nth-last-child(-n+2):nth-child(odd) + .item-row { border-bottom: 0; }

/* The world map and the social boards. The map plate inside the world map is drawn
   to whatever width the window gives it, so the window is the measurement and the plate follows. */
.wm { position: fixed; left: 50%; top: 8%; transform: translateX(-50%); width: 46em; }
/* Where a creature stands, one dot a band, answered by the server when a quest objective is
   pressed (2026-09-13). A ring of light around the ink so the dot reads on sand, water, grass and
   forest alike - the plate is a painting and the marks have to survive all of it. */
.wm-mark {
  position: absolute; width: .6em; height: .6em; transform: translate(-50%, -50%);
  border-radius: 50%; background: #bb6e66;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, .9), 0 1px 2px rgba(60, 25, 12, .55);
}
.so { position: fixed; left: 50%; top: 10%; transform: translateX(-50%); width: 54em; max-width: calc(100vw - 1.867em); }
/* Direct Message. The one window a player keeps open WHILE playing, so it stands out of the way
   in the bottom-right corner rather than over the middle of the world - and it stands directly
   over its own folded heads, so putting a conversation away and taking it back out is one place
   on screen rather than two.

   What it clears is the bottom-right corner the HUD owns: 10px of edge, the 44px menu row, and
   the 53px row of folded heads over it, plus 8px of air. Those are public/hud.css's own pixels
   (--hud-edge, --hud-dm-row, and .talk-head at 3.4em of the HUD's 14px), restated here as
   literals rather than read across, which is this sheet's rule and the reason it survives
   hud.css not loading. They are scaled by the same --gs-hud-scale the HUD is. */
.dm {
  position: fixed; right: 1.2em;
  bottom: calc(115px * var(--gs-hud-scale, 1));
  width: 44em; max-width: calc(100vw - 1.867em);
}
/* Two rows of menu tiles at this width, so the corner is 41px deeper. Same breakpoint hud.css
   uses for .hud-menu, and it has to be restated for the same reason the numbers above are. */
@media (min-width: 761px) and (max-width: 1300px), (hover: none) and (pointer: coarse) and (min-width: 761px) {
  .dm { bottom: calc(156px * var(--gs-hud-scale, 1)); }
}
.gr { position: fixed; left: 50%; top: 12%; transform: translateX(-50%); width: 42em; max-width: calc(100vw - 1.867em); }
.gu { position: fixed; left: 50%; top: 6%; transform: translateX(-50%); width: 58em; max-width: calc(100vw - 1.867em); }
.gf { position: fixed; left: 50%; top: 14%; transform: translateX(-50%); width: 28em; max-width: calc(100vw - 1.867em); }

/* Nothing in the party rail itself is placed: the gear, both menus and every frame are rows of
   the rail's own grid, so a menu opens under the thing it belongs to with no measured offset.
   The one question the party asks is placed, over every window, at the dialog layer. */
.pty-ask { position: fixed; left: 50%; top: 22%; transform: translateX(-50%); width: 26em; z-index: 80; }

.sgn { position: fixed; left: 50%; top: 6%; transform: translateX(-50%); width: 46em; max-width: calc(100vw - 1.867em); }
.war { position: fixed; left: 50%; top: 10%; transform: translateX(-50%); width: 38em; max-width: calc(100vw - 1.867em); }
/* THE WINDOW FOLLOWS THE TAB. A bracket is a wide thing and a roster of five is not: one width
   for both means either a squashed bracket or a mostly empty window for everything else. */
.cup { position: fixed; left: 50%; top: 8%; transform: translateX(-50%); width: 48em; max-width: calc(100vw - 1.867em); }
.cup[data-tab="bracket"] { width: 72em; }

/* Profile and activity. */
.pf {
  position: fixed; left: 50%; top: 4%; transform: translateX(-50%);
  width: 42em; max-width: calc(100vw - 1.867em);
}
.act { position: fixed; left: 50%; top: 10%; transform: translateX(-50%); width: 34em; max-width: calc(100vw - 1.867em); }
/* Centred on both axes and clipped to the viewport: this window grows a section at a time, and a
   fixed `top` ran each new one further down the screen until the last was off the bottom. */
.opt {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 44em; max-width: calc(100vw - 1.867em); max-height: min(46em, calc(100vh - 3.733em));
}

/* The skill panel and the skill card that follows the pointer out of it. The talents page is a
   three-lane tree and wants more room than the skill list; it follows the tab the way the cup's
   bracket does, so the phone block below can take both widths off with one rule. */
.sp { position: fixed; left: 50%; top: 12%; transform: translateX(-50%); width: 44em; }
.sp[data-tab="belief"] { width: 52em; }

/* A PHONE HELD SIDEWAYS keeps the desktop positions above, and has 400px of height to put them in.
   No window may be taller than the screen less a gutter each end, and its body is the scroller so
   the head stays in reach to drag it by (panel.ts keepOnScreen puts it back inside on open). The
   marketplace keeps its own inner scrollers, as above. */
@media (hover: none) and (pointer: coarse) and (min-width: 761px) {
  .win { max-height: calc(100dvh - 32px); }
  .win > .win__body { min-height: 0; overflow-y: auto; }
  .market > .win__body { overflow-y: visible; }
  /* The three windows that read at their own 14px shrink by the phone's factor like every other
     (orientation.ts writes --win-k beside --win-size). */
  .pd, .pr, .market { --win-size: calc(14px * var(--win-k, 1)); }
  /* A floating card - a tooltip, the menu a body or a row opens - sets no size and so read at the
     page's 15px, a third bigger than the window it came out of. It reads at the window size. */
  .card { font-size: var(--win-size); max-height: calc(100dvh - 16px); overflow: hidden; }
  /* A worn piece's card was still two thirds of the screen at that size: a star row set in fixed
     px by gs.css (16px a star, 34px tall), a 19px line per stat and six socket lines. The stars
     come down to the card's own type, the stat and socket lines lose most of their air, and the
     unpunched sockets say it once instead of up to six times. `[data-stars]` so this outranks
     gs.css's tier rules, which name up to three classes. */
  .card .gs-tooltip__stars[data-stars] { min-height: 0; }
  .card .gs-tooltip__stars[data-stars] i { font-size: 10px; letter-spacing: 1px; padding-block: 1px; }
  .card .gs-tooltip__stars[data-stars] .gs-star { margin-right: 1px; }
  /* The two-row rating that used to live here is gs.css's now: the card is 17em on every screen,
     so past ten stars it never fitted one row anywhere. Only the type size below is about phones. */
  .card .kv > div { padding-block: .15em; }
  .card .item-row { padding-block: .15em; }
  .card .item-row--empty ~ .item-row--empty { display: none; }}

/* ================================================================= MOBILE SHEETS
   Desktop windows keep their authored positions above 760px. On a narrow screen every substantial
   window becomes one viewport-safe sheet: no panel may depend on a desktop left/right offset, and
   tall content scrolls inside the sheet instead of disappearing below the gesture bar. A phone held
   sideways is laid out wide (orientation.ts) and keeps the desktop positions, scaled with the page. */
@media (max-width: 760px) {
  :root { --place-gutter: .4em; }

  .bag, .warehouse, .cs, .ws, .ql, .pt, .ps, .pr, .pd, .shop, .market,
  .gth, .cmp, .ml, .auc, .stall, .trd, .ie, .wm, .so, .gu,
  /* `.cup[data-tab="bracket"]` is more specific than this list, so its desktop width would win
     here and pin a phone to 82em. Said again at equal specificity rather than by weakening the
     desktop rule: the bracket really does want that width when there is a screen for it. */
  .cup[data-tab="bracket"],
  /* The same trap, again: `.pd.pd--fusion` is two classes against this list's one, so the fusion
     tab's 58em would pin a phone just as the bracket would. */
  .pd.pd--fusion { width: auto; }
  .sp, .sp[data-tab="belief"] { width: auto; }
  .pty-ask, .sgn, .war, .cup, .act, .opt, .dlg, .dlg-svc {
    left: max(var(--place-gutter), env(safe-area-inset-left));
    right: max(var(--place-gutter), env(safe-area-inset-right));
    top: max(var(--place-gutter), env(safe-area-inset-top));
    bottom: auto;
    width: auto;
    min-width: 0;
    max-width: none;
    max-height: calc(100dvh - max(.8em, env(safe-area-inset-top)) - max(.8em, env(safe-area-inset-bottom)));
    height: auto;
    transform: none;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* Panels commonly opened together share one sheet position. The newest one already wins through
     the panel stack, so keeping the desktop side-by-side offsets would only clip both. */
  .warehouse, .ps { right: max(var(--place-gutter), env(safe-area-inset-right)); }

  /* The guild panel is the tallest of them and starts higher up the screen for it. */
  .gu { top: 2%; max-height: 96vh; overflow-y: auto; }
  .market { max-height: calc(100dvh - .8em); }

  /* The party rail cannot hold five frames on a phone: past 30dvh the list scrolls inside itself
     rather than pushing chat off the bottom. Found by its hook and not by a class, because the
     rail is the design system's `.rail` now and that name is also the player's own block. */
  [data-party-rail] { max-height: 30dvh; overflow-x: hidden; overflow-y: auto; }
}


/* --- the price line ---------------------------------------------------------------------------
   THE ONE THING IN THE GAME THE DESIGN SYSTEM DOES NOT DRAW. The Marketplace's chart is an inline
   SVG the panel builds itself (marketplace.ts drawChart): a line, a soft fill under it and a
   dashed grid. These five rules used to live in public/panels.css and were the last thing the
   market still read out of the old stone sheet; they are restated here against the design's own
   hues - the sage and rose the buttons wear - so the sheet could go.

   The scale and the date row are NOT positioned here: the chart's well is a three-row grid and
   they are the first and last row of it. The SVG only has to fill the middle one. */
.market-chart { width: 100%; height: 100%; min-height: 0; overflow: visible; }
.market-chart__grid { stroke: var(--rule, #d8c6b2); stroke-width: 1; stroke-dasharray: 3 5; opacity: .55; }
.market-chart__line { fill: none; stroke: #7d946e; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.market-chart__area { fill: #7d946e; opacity: .13; stroke: none; }
.market-chart.is-down .market-chart__line { stroke: #bb6e66; }
.market-chart.is-down .market-chart__area { fill: #bb6e66; }
