div.panel {
  width: 50%;
}

div.zone_pane {
  text-align: center;
}

.form-card-wrap {
  max-width: 480px;
}

/* Map grid - styled as a parchment map spread on a dark table, per the
   world map view's "high-fantasy adventure" pass. .map-frame is the wooden/
   gilt frame + parchment panel; .grid/.cell/.cell a are the actual tiles.
   Colors come from base/css/styles.css's --ioe-map-* tokens so the whole
   panel re-themes with the rest of the site (see accounts.Profile.theme). */
.map-title {
  text-align: center;
  font-family: Georgia, 'Palatino Linotype', 'Book Antiqua', serif;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--ioe-map-title);
  text-shadow: 0 0 8px color-mix(in srgb, var(--ioe-map-title) 40%, transparent), 0 1px 0 rgba(0, 0, 0, 0.6);
  margin-bottom: 0.75rem;
}

/* Centers the map-frame + legend as a pair rather than the frame alone -
   .map-frame keeps its own margin below for when it's the only child. */
.map-and-legend {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 1.5rem;
}

.map-frame {
  display: flow-root; /* .grid's floats need a real height to sit inside the frame/border */
  position: relative; /* containing block for #weatherLayer, which overlays #mapGrid's box exactly since #mapGrid is .map-frame's only in-flow content */
  /* A flex item's width defaults to content-based ("shrink-to-fit") unless
     given a definite size - without this, .grid's percentage widths had no
     definite containing block to resolve against and collapsed to ~0
     (only the current-room marker's own content kept that one tile
     visible). flex-basis 720px + shrink is what max-width:720px alone
     used to provide back when this was a plain block box. */
  flex: 1 1 720px;
  width: 720px;
  max-width: 100%;
  margin: 0 0 1.5rem;
  padding: 20px;
  border: 6px double var(--ioe-map-frame-border);
  border-radius: 6px;
  background:
    radial-gradient(ellipse at top left, rgba(255, 255, 255, 0.15), transparent 60%),
    radial-gradient(ellipse at bottom right, rgba(0, 0, 0, 0.25), transparent 60%),
    var(--ioe-map-parchment);
  box-shadow: 0 0 30px rgba(0, 0, 0, 0.7), inset 0 0 40px color-mix(in srgb, var(--ioe-map-frame-border) 35%, transparent);
}

#mapGrid {
  overflow: hidden; /* makes #mapGrid establish its own BFC so it properly contains its floated .grid cells' height, instead of collapsing to 0 */
}

/* Staff editor's pan-arrow cross (world.views._editor_pan_links,
   room_selection_editor.html) - re-centers the same player-centered
   window _grid_window_center() would otherwise always pick, so staff can
   reach a region of the zone their own character isn't standing in. Sits
   below the map frame (a secondary control, not competing with the map
   itself for top billing). One D-pad-style control: 4 solid arrowhead
   wedges (one shared <symbol>, drawn pointing north and rotated per
   direction below, rather than redrawn 4 times) with their tails flush
   against the center cell - zero grid gap is what actually makes them
   butt up against the recenter circle instead of floating separately. */
.editor-pan-cross {
  display: grid;
  grid-template-columns: repeat(3, 48px);
  grid-template-rows: repeat(3, 48px);
  gap: 0;
  width: fit-content;
  margin: 1.25rem auto 0;
}

.editor-pan-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
}

.editor-pan-icon {
  width: 100%;
  height: 100%;
  padding: 2px;
  box-sizing: border-box;
  fill: var(--ioe-exit-color);
  transition: fill 0.1s ease, transform 0.1s ease;
}

.editor-pan-arrow:hover .editor-pan-icon {
  fill: var(--ioe-exit-hover-color);
  filter: drop-shadow(0 0 4px var(--ioe-map-hover-glow));
}

.editor-pan-arrow:active .editor-pan-icon {
  transform: scale(0.9);
}

/* Base <symbol> is drawn pointing north, tail at its own bottom edge -
   rotating it in place keeps that tail against whichever edge borders
   the center cell for that direction (e.g. rotated 90° for east, the
   tail that was "south" is now "west" - against the center). */
.editor-pan-icon-e { transform: rotate(90deg); }
.editor-pan-icon-s { transform: rotate(180deg); }
.editor-pan-icon-w { transform: rotate(270deg); }
.editor-pan-arrow:active .editor-pan-icon-e { transform: rotate(90deg) scale(0.9); }
.editor-pan-arrow:active .editor-pan-icon-s { transform: rotate(180deg) scale(0.9); }
.editor-pan-arrow:active .editor-pan-icon-w { transform: rotate(270deg) scale(0.9); }

/* Recenter hub - a ring+dot (not solid, unlike the 4 arrows) so it reads
   as the fixed center the arrows surround rather than a 5th direction.
   Sized well under the cell so the touching arrow tails frame it with a
   sliver of parchment showing, instead of the ring butting flush too. */
.editor-pan-icon-reset {
  width: 30px;
  height: 30px;
  padding: 0;
  fill: none;
  stroke: var(--ioe-map-frame-border);
  stroke-width: 2.5;
}

.editor-pan-arrow:hover .editor-pan-icon-reset {
  fill: none;
  stroke: var(--ioe-exit-hover-color);
}

.editor-pan-n     { grid-column: 2; grid-row: 1; }
.editor-pan-w     { grid-column: 1; grid-row: 2; }
.editor-pan-reset { grid-column: 2; grid-row: 2; }
.editor-pan-e     { grid-column: 3; grid-row: 2; }
.editor-pan-s     { grid-column: 2; grid-row: 3; }

/* Falling-weather overlay (see base/js/weather/Snow.js's initSnow()) - a
   sibling of #mapGrid rather than a child, since grid.js owns #mapGrid's
   contents wholesale. Positioned against .map-frame instead, which is
   equivalent here since #mapGrid is .map-frame's only in-flow content. */
#weatherLayer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none; /* let clicks reach the room tiles underneath */
  z-index: 2;
}

/* Legend box - only lists Environments actually present on this zone's
   map (see world.views._legend_for_rooms), so it never grows past what's
   relevant to the map currently on screen. Shares the map-frame's
   parchment/wood-frame look so the two read as one panel. */
.map-legend {
  flex: 0 0 auto;
  min-width: 140px;
  margin-top: 20px;
  padding: 12px 16px;
  border: 4px double var(--ioe-map-frame-border);
  border-radius: 6px;
  background: var(--ioe-map-parchment);
  color: var(--ioe-map-frame-border);
}

.map-legend h3 {
  margin: 0 0 0.5rem;
  font-family: Georgia, 'Palatino Linotype', 'Book Antiqua', serif;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 1rem;
  text-align: center;
}

.map-legend table {
  border-collapse: collapse;
}

.map-legend td {
  padding: 2px 8px 2px 0;
  font-size: 0.9rem;
  white-space: nowrap;
}

.legend-swatch {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  border: 1px solid var(--ioe-map-frame-border);
  vertical-align: middle;
}

/* Miniature of .cell.current-room + .current-room-marker (same flat
   yellow, not a theme token, and the same two-bar X) so the legend entry
   actually matches the mark drawn on the grid tile. */
.legend-swatch-current {
  position: relative;
  background-color: #f7d716;
}

.legend-swatch-current::before,
.legend-swatch-current::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 10%;
  width: 80%;
  height: 18%;
  background-color: #000;
  border-radius: 1px;
}

.legend-swatch-current::before {
  transform: translateY(-50%) rotate(45deg);
}

.legend-swatch-current::after {
  transform: translateY(-50%) rotate(-45deg);
}

/* Echoes .cell.wall-* at legend scale - the base .legend-swatch already
   has a 1px border on all sides in --ioe-map-frame-border, so only the
   bottom edge needs thickening to read as "a wall segment". */
.legend-swatch-wall {
  background-color: var(--ioe-map-tile-empty);
  border-bottom-width: 4px;
}

/* Miniature of .enemy-dot - same flat red, not a theme token. */
.legend-swatch-enemy {
  background-color: #c81e1e;
  border-radius: 50%;
}

.grid {
  float: left;
  position: relative;
  width: 5%; /* 1 / GRID_WINDOW (world.views.GRID_WINDOW = 20) - the map grid is a fixed-size window, not sized to a zone's actual extent, so this stays a plain constant rather than needing to track zone size */
  aspect-ratio: 1 / 1; /* real square tiles - percentage height had no ancestor to resolve against */
  outline: 1px solid var(--ioe-map-grid-line);
  overflow: hidden;
}

.cell {
  position: absolute;
  inset: 0;
  box-sizing: border-box; /* keeps wall/edge borders (below) flush with .grid's clipped bounds */
  background-color: var(--ioe-map-tile-empty); /* faint tint for an unoccupied tile */
}

.cell li {
  list-style: none;
}

.cell a {
  position: absolute;
  inset: 0;
  display: block;
}

.cell a[href] {
  cursor: pointer;
}

.cell a[href]:hover {
  outline: 2px solid var(--ioe-map-hover-glow);
  box-shadow: inset 0 0 12px 2px color-mix(in srgb, var(--ioe-map-hover-glow) 60%, transparent);
  z-index: 1;
}

/* Marks the viewing user's active character's current room - see
   world.views._build_room_matrix's `current` flag and grid.js. The
   yellow fill is a flat, fixed color (not a theme token) so it reads as
   "you are here" consistently regardless of the tile's own environment
   color or the active site theme. */
.cell.current-room {
  background-color: #f7d716 !important;
}

/* Sits above the hover outline (z-index 2) so it stays visible on hover;
   pointer-events is off so it never intercepts the tile's own click/hover.
   Empty div - the X itself is drawn by ::before/::after below rather than
   a text glyph (e.g. "\2715"), which depends on the server declaring the
   right charset for a static .js file to render correctly - not something
   worth risking for a decorative mark. */
.current-room-marker {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60%;
  height: 60%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 2;
}

.current-room-marker::before,
.current-room-marker::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 18%;
  background-color: #000;
  border-radius: 2px;
}

.current-room-marker::before {
  transform: translateY(-50%) rotate(45deg);
}

.current-room-marker::after {
  transform: translateY(-50%) rotate(-45deg);
}

/* Enemy Compass overlay (see world.views._build_room_matrix's `has_enemy`
   flag / grid.js) - a small solid dot marking a room with a currently-
   spawned NPC. Flat, fixed colors (not theme tokens) for the same reason
   as .cell.current-room's yellow - a "danger" marker needs to read
   consistently regardless of the tile's own environment color or the
   active site theme, and the two can coexist (your own room can also
   have an enemy in it) without the dot getting lost against the yellow
   fill. pointer-events off so it never blocks the tile's anchor. */
.enemy-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26%;
  height: 26%;
  transform: translate(-50%, -50%);
  background-color: #c81e1e;
  border: 1px solid #000;
  border-radius: 50%;
  pointer-events: none;
  z-index: 2;
}

/* Wall/barrier between two occupied, geometrically-adjacent rooms with no
   connecting exit (see world.views._room_walls). Same --ioe-map-frame-border
   token as the rest of the map chrome (.map-frame/.map-legend borders) -
   theme-reactive but deliberately NOT [data-style]-reactive, consistent
   with every other --ioe-map-* token (the world map is its own "parchment
   on a dark table" identity, independent of the Nature/Magic visual-style
   reskins). 4px solid matches this file's existing border-width scale. */
.cell.wall-n { border-top:    4px solid var(--ioe-map-frame-border); }
.cell.wall-e { border-right:  4px solid var(--ioe-map-frame-border); }
.cell.wall-s { border-bottom: 4px solid var(--ioe-map-frame-border); }
.cell.wall-w { border-left:   4px solid var(--ioe-map-frame-border); }

/* A side facing a genuinely empty/unmapped cell - lighter and dashed so it
   reads as "unexplored", clearly distinct from a real wall above. */
.cell.edge-n { border-top:    2px dashed color-mix(in srgb, var(--ioe-map-frame-border) 55%, transparent); }
.cell.edge-e { border-right:  2px dashed color-mix(in srgb, var(--ioe-map-frame-border) 55%, transparent); }
.cell.edge-s { border-bottom: 2px dashed color-mix(in srgb, var(--ioe-map-frame-border) 55%, transparent); }
.cell.edge-w { border-left:   2px dashed color-mix(in srgb, var(--ioe-map-frame-border) 55%, transparent); }

/* .map-frame is width:720px with max-width:100%, so on a small/mobile
   viewport each .grid tile (5% of the frame) can shrink well below its
   desktop size while wall/edge border-widths above stay fixed - at tile
   sizes that small, the 4px wall border reads as disproportionately
   thick. Scaled down 1:1 with the desktop 4px:2px wall:edge ratio so the
   "real wall vs unexplored edge" distinction still reads at this size. */
@media (max-width: 576px) {
  .cell.wall-n { border-top-width:    2px; }
  .cell.wall-e { border-right-width:  2px; }
  .cell.wall-s { border-bottom-width: 2px; }
  .cell.wall-w { border-left-width:   2px; }

  .cell.edge-n { border-top-width:    1px; }
  .cell.edge-e { border-right-width:  1px; }
  .cell.edge-s { border-bottom-width: 1px; }
  .cell.edge-w { border-left-width:   1px; }
}

.room-view {
  font-family: 'Courier New', Courier, monospace;
  max-width: 640px;
  color: var(--ioe-text);
}

/* Room detail page's 3x3 mini-map (world.views._room_neighborhood) - a
   fixed-size, non-interactive reflection of the full zone .grid/.cell's
   env-color/current-room convention, so it reads as a small piece of the
   same map rather than a different widget. */
.mini-map {
  display: grid;
  grid-template-columns: repeat(3, 28px);
  grid-template-rows: repeat(3, 28px);
  gap: 2px;
  width: fit-content;
  margin-bottom: 0.75rem;
  padding: 6px;
  border: 3px double var(--ioe-map-frame-border);
  border-radius: 4px;
  background: var(--ioe-map-parchment);
}

.mini-map-cell {
  background-color: var(--ioe-map-tile-empty);
  outline: 1px solid var(--ioe-map-grid-line);
  position: relative; /* anchor for .mini-map-move below */
}

/* Same flat "you are here" yellow as .cell.current-room. */
.mini-map-cell-current {
  background-color: #f7d716 !important;
  outline: 2px solid #000;
}

/* Click-to-move overlay on the mini-map's 4 cardinal edge cells (see
   world.views.UserRoomView/_room_neighborhood) - only rendered at all when
   that direction is a currently open exit the viewer can use, so a
   corner/center cell (no 'side') and a cardinal cell with no matching
   open exit both stay plain, non-interactive tiles. */
.mini-map-move {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

.mini-map-move:hover {
  outline: 2px solid var(--ioe-map-hover-glow); /* same hover language as the full zone grid's .cell a[href]:hover */
}

.mini-map-arrow {
  font-weight: bold;
  color: var(--ioe-exit-color); /* same token .room-exits already uses - re-themes correctly under every visual style */
  /* Solid outline ring (not a single blurred shadow) so the glyph stays
     legible against any arbitrary Environment.color background. */
  text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}

.mini-map-move:hover .mini-map-arrow {
  color: var(--ioe-exit-hover-color);
}

.room-title {
  font-weight: bold;
}

/* --ioe-exit-color/--ioe-exit-hover-color default to
   --ioe-success-strong/--ioe-map-hover-glow (base/css/styles.css :root) -
   same look as before everywhere except [data-style="fantasy_nature"],
   which repoints them to stay legible against Nature's light page-panel
   background (see that override's comment). */
.room-exits {
  color: var(--ioe-exit-color);
}

/* An exit only turns into a link when the viewer's active character is
   actually standing in this room (world.views.UserRoomView's `is_here`) -
   inherits the exits line's own green rather than the browser's default
   blue link color, with a dotted underline as the only "this is
   clickable" cue since it's inline with plain exit-name text. */
.room-exits a {
  color: inherit;
  text-decoration: underline;
  text-decoration-style: dotted;
}

.room-exits a:hover {
  color: var(--ioe-exit-hover-color);
  text-decoration-style: solid;
}

/* Bottom-of-page player HUD (world.views.UserRoomView's toon_level/
   toon_health/toon_energy/hud_actions context) - a wide compass-dial
   ellipse split into 4 pie sections by a drawn X (room-hud-divider-a/-b,
   rotated +/-atan(height/width) so each line runs corner-to-corner of the
   dial's own bounding box, clipped to the ellipse by overflow: hidden),
   one stat/action per compass point. Any currently-available gadget/
   ability/other-unlockable action (world.views._hud_actions_for() - today
   just the Cartographer's Kit's Open Map / buy-map prompt) renders below
   it in .room-hud-actions, not inside the dial, since a dial slot has no
   room to grow into a variable-width button. Parchment-and-double-border
   reuses .mini-map's cartography look on purpose - both dial and action
   bar read as the same "cartographer's instrument" object. */
.room-hud {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin: 1.5rem auto 0.5rem;
}

.room-hud-dial {
  position: relative;
  width: 300px;
  height: 150px;
  max-width: 90vw;
  overflow: hidden;
  border-radius: 50%;
  background: var(--ioe-map-parchment);
  border: 3px double var(--ioe-map-frame-border);
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
}

/* Long enough to clear the dial's own diagonal (sqrt(300^2+150^2) =~
   335px) after rotation, however the dial happens to be scaled down by
   max-width on a narrow viewport - overflow: hidden on .room-hud-dial
   clips both lines back down to the ellipse either way. */
.room-hud-divider {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200%;
  height: 1px;
  background: var(--ioe-map-frame-border);
  transform-origin: center;
}
/* atan(150/300) - the exact corner-to-corner angle of the 300x150 dial
   above; keep in sync if those dimensions ever change. */
.room-hud-divider-a { transform: translate(-50%, -50%) rotate(26.565deg); }
.room-hud-divider-b { transform: translate(-50%, -50%) rotate(-26.565deg); }

.room-hud-slot {
  position: relative; /* stacks above the dividers */
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.15;
}
.room-hud-slot-top    { grid-column: 2; grid-row: 1; }
.room-hud-slot-left   { grid-column: 1; grid-row: 2; }
.room-hud-slot-right  { grid-column: 3; grid-row: 2; }
.room-hud-slot-bottom { grid-column: 2; grid-row: 3; }

.room-hud-label {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--ioe-map-frame-border) 65%, transparent);
}

.room-hud-value {
  font-weight: bold;
  font-size: 1.05rem;
  color: var(--ioe-map-frame-border);
}

.room-hud-slot-link {
  font-weight: bold;
  color: var(--ioe-map-frame-border);
  text-decoration: underline;
  text-decoration-style: dotted;
}
.room-hud-slot-link:hover {
  color: var(--ioe-map-frame-border);
  text-decoration-style: solid;
}

/* Bordered row of gadget/ability/other-unlockable actions
   (world.views._hud_actions_for()) - only rendered at all while it has
   at least one entry (see room_view_user.html), so an empty bar never
   shows as an empty box. Same double-border/parchment treatment as
   .room-hud-dial/.mini-map, driven entirely by the --ioe-map-* theme
   variables (base/static/base/css/styles.css's [data-theme] blocks), so
   it re-themes automatically with the rest of the cartography look -
   nothing here is a color literal. */
.room-hud-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.75rem;
  background: var(--ioe-map-parchment);
  border: 3px double var(--ioe-map-frame-border);
}
