/* ⭐CASCADE LAYERS (#2920, 2026-09-22): this file is the `skin` layer. The
   order is declared once, below, and repeated inline by build-web.js so
   the world page fixes it before any stylesheet loads. Inside a layer,
   load order and specificity still decide between THIS file's rules;
   between files, the layer decides, always. */
@layer base, module, skin, nerm, corners;
@layer skin {
/* ══════════════════════════════════════════════════════════════════════
   WORLD SKIN — the shell in Medieval Majesty's navy and gold (#2020).
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-28, picking variant A from round 14: the drawn chrome
   direction (#1953) finally has artwork that fits it.

   THIS FILE ONLY RE-SKINS. It adds no element, moves nothing between
   parents, and world-shell.js is untouched by it. Everything below is a
   rule against markup that already exists, so turning the flag off leaves
   the old shell exactly as it was, and any future change to what the rail
   CONTAINS lands in one place rather than two.

   ── WHY THESE PIECES AND NOT A GRADIENT ───────────────────────────────
   The measured fault (#2020) was never hue: the old nav tile sat at
   1.49:1 against its own rail, and 3:1 is the floor for a control to
   read as a control. Cozy's button carries a one-pixel gold rule along
   its TOP edge at 5.35:1 against this ground, which is what makes the
   tile legible; its navy FILL is 1.59 and does no work at all. So do not
   "simplify" this to a flat background colour later. The edge is the
   whole mechanism, and it is a nine-patch because the edge has to stay
   one pixel at every size.

   ── THE SCALE IS NOT A PREFERENCE ─────────────────────────────────────
   Every piece is 16x16 native shown at x4, because the render upscales
   x4 and chrome that is smoother than the picture reads as a sticker on
   a photograph (#1953 decision 3). A nine-patch also cannot be drawn
   smaller than twice its corner: at corner 4-5 that is a 24x36 floor,
   which is why a 52px tile works here where Medieval Majesty's own menu
   button (corner 8, so a 64px floor with zero content height) could not.
   If a size below 52px is ever wanted, the artwork has to change, not
   the CSS.

   ── ATTRIBUTION IS LOAD-BEARING ───────────────────────────────────────
   Medieval Majesty is CC BY 4.0 and REQUIRES credit. See ATTRIBUTION.md.
   ══════════════════════════════════════════════════════════════════════ */

/* New Rocker (SIL OFL, licence beside the file), the game's title
   blackletter since 2026-10-04: Wray picked it over the old pixel face,
   Not Jam Blackletter, which players did not like. It is a smooth face, so
   the old rule that it may only be shown at 16, 32, 48 or 64 px no longer
   applies; sizes still sit on that grid because they were set for it. */
@font-face {
  font-family: 'New Rocker';
  src: url('/assets/fonts/NewRocker-Regular.ttf?v=2026-10-04a') format('truetype');
  /* `optional` keeps the fallback for the page's LIFETIME on a cold
     cache, so the tome face never appeared even though the file had
     loaded. `swap` plus the <link rel=preload> in world.html: the
     preload usually wins the race, and when it does not the name
     still arrives instead of silently never arriving. */
  font-display: swap;
}

body.skin-majesty {
  /* Majesty's own values, verbatim, so a Cozy button and a Majesty window
     share edge colours rather than merely resembling each other. */
  --sk-navy: #182029;
  --sk-navy-lift: #2b3647;
  --sk-gold: #a28155;
  --sk-gold-hi: #caa982;
  --sk-ink: #f0e2c6;
  --sk-ink-dim: #9d8a6d;
  --sk-ink-on-gold: #20180c;
  --sk-ground: #0a0e13;
  --sk-hot: #e03c31;

  --sk-tome: 'New Rocker', 'Press Start 2P', monospace;

  /* ⭐THE OTHER TWO FACES, WHICH DID NOT EXIST UNTIL 2026-09-07.
     ─────────────────────────────────────────────────────────────────
     `--sk-head` and `--sk-body` were read by 74 rules in this file and
     defined by none of them; every use is `var(--sk-head, 'Press Start 2P',
     monospace)`, so the FALLBACK has been doing the work since the skin
     shipped. `--wc-head` / `--wc-body` were worse: `/ui-kit.html` section 6
     publishes them as the world's two faces to anyone building a surface,
     `world-albion.css` chains `--an-head` off one of them, and neither was
     ever written down anywhere in the repo.

     Nothing here changes what renders. Each value is the fallback chain its
     own readers already carry, so this is a no-op by construction -- and a
     test pins that, comparing every `var(--sk-head, …)` fallback in this file
     against what is declared here. What it buys is the thing the type review
     (#2557, kit section 10) needs: ONE place to re-point the world's faces,
     instead of 74 fallbacks to find and edit.

     `--wc-*` are the public names, because that is what the kit documents and
     what a new surface is told to read; the `--sk-*` pair alias them so this
     file's own rules keep the names they already use. */
  --wc-head: 'Press Start 2P', 'Courier New', monospace;
  --wc-body: 'VT323', 'Courier New', monospace;
  --sk-head: var(--wc-head);
  --sk-body: var(--wc-body);

  /* one owner for the tray's height: the rails stop above it and the tray is
     it, so the two cannot drift apart into an overlap or a gap */
  --sk-tray: 50px;

  /* one owner for the window's size, so every door opens the same shape and
     a smaller one is a single edit rather than a hunt */
  /* what the whole-multiple picture leaves under everything */
  /* OVERRIDABLE (Wray, 2026-09-05, after widening the bar in the lab and
     watching it cover the target and bury the chat): the computed band is
     the default; --sk-footh, driven by the lab's `bottom bar` slider, wins
     when set -- and because the RAIL HEIGHTS, the TARGET PANE and the CHAT
     all read --sk-foot, widening the bar now moves everything above it
     instead of over it. */
  /* THIN AGAIN, and that is the ruling (Wray, 2026-09-05: "the foot strip
     stays thin"). It holds the say line and the counts; the chat HISTORY
     went up the left rail where it can grow, which is what a tall bar was
     failing to give it. The letterbox remainder is the default; the lab's
     `bottom bar` slider still overrides. */
  /* 38, HIS NUMBER (lab save 2026-09-04T18:09Z, `#wt-foot` height 38). The
     letterbox remainder was never "thin" -- it is whatever the whole-number
     upscale leaves, 130px on a 900px window -- so the strip only became the
     thin thing he asked for once it stopped being that leftover. What is
     left between the picture and the strip is the band Nerm lives in. */
  --sk-foot: var(--sk-footh, 38px);
  /* How much of the right gutter the pinned target reserves — ZERO until
     something is selected. Reserved unconditionally it took 250px from a
     637px gutter that also holds the map, the journal and Nerm, and the
     journal collapsed to a strip to pay for a panel that was not on screen. */
  --sk-target: 0px;
  /* BIGGER, AND IT DOES NOT BREAK. Wray: "I dont suppose we can make the
     parchment bigger or it will break right?" It will not: the page stopped
     being a stretched 96px scrap when it became a nine-slice, so the torn
     edge stays 16 art-pixels deep at any size and only the middle grows.
     These two numbers are now the only thing that decides how big a window is,
     which is also what makes them worth putting in the layout lab. */
  /* ⭐THE ASK IS A SHARE OF THE PICTURE, NOT A PIXEL COUNT (#2557,
     2026-09-08). Wray, after getting into the real Albion client for the
     first time: "2612 sounds like measuring the exact albion frame
     proportions into our game? sounds like the best way to go."

     THE ONE MEASUREMENT WE HAVE. Albion's Stats window is 324 x 576 on a
     1509 x 923 logical page: 21.5% of the width, 62.4% of the height. It
     floats clear of every edge (left gap 590, right 596, top 185, bottom
     163) and it cannot be dragged, so its size and its place are both
     design decisions rather than a user's.

     A PERCENTAGE, BECAUSE A CONSTANT ONLY LANDS AT ONE RESOLUTION. The same
     lesson the nav ribbons already paid for further down: the picture is a
     whole multiple of 320x240, so it changes in big steps, and a window
     pinned in px is 27% of the screen on one monitor and 40% on another.
     Albion's proportion is what we are copying, so a proportion is what we
     should store.

     ⛔THIS REVERSES A NUMBER OF HIS OWN, KNOWINGLY. On 2026-09-06 he asked
     for the side windows to be LONGER ("so you can fit the info to those
     boards") and the list doors went to 760-880, which the clamp then let
     fill the whole band. The measurement says an Albion window is 62% of
     the height and floats clear top and bottom; it does not fill. His
     09-08 ruling is the later one and it is the one being applied. The
     rows are not lost by it: `.ww-table` is `overflow-y: auto`, so a
     shorter board shows fewer rows before it scrolls rather than slicing
     one, which is the bug the 09-06 ask was actually fixing.

     ⛔ONE MEASURED WINDOW IS ONE NUMBER, NOT EIGHT. Only the readout class
     below is Albion's shape directly. The other three are derived from what
     OUR doors hold -- a grid of eight bag columns is wider than a label and
     a value, the same reason their Appearance window is wider than their
     Stats -- and they are pulled toward the reference rather than invented
     from it. Do not read the other three as measurements.

     The floors are the small-window case: below them a share of the picture
     stops being enough to draw the content at all, and the ceiling is still
     the clamp on `--sk-win-cw` further down, so a phone is unaffected. */
  --sk-win-wpct: .26;
  --sk-win-hpct: .62;
  --sk-win-wmin: 400px;
  --sk-win-hmin: 320px;
  /* ⛔THE ASK IS DERIVED ON THE SHEET, NOT HERE, AND THAT IS LOAD-BEARING.
     A custom property resolves where it is DECLARED. Computing the ask in
     this block would resolve `--sk-win-wpct` against this block's value, so
     every door would inherit one already-finished number and a per-door
     share would reach nothing -- measured 2026-09-08 with every one of the
     eight doors reporting an identical 26% x 62%. The derivation therefore
     lives beside `--sk-win-cw` on `.ws-sheet` and `.ws-tabs`, where the
     door's own share is in scope.

     `--sk-win-w` / `--sk-win-h` survive as the LAYOUT LAB's channel:
     world-layout-lab.js writes them inline on <body> (its `window width` and
     `window height` sliders), and the derivation reads them as an override
     with the share as its fallback. Set, they win; unset, the share is used.
     Declaring them here would make them always set and silently retire the
     whole system, which is the trap above wearing a different hat. */
  /* WHAT A DOOR ASKS FOR IS NOT WHAT IT GETS (#2557). The two numbers above
     are the ASK: the layout lab drives them, and a door with a shape of its
     own overrides them on `.ws-sheet[data-door]` AND `.ws-tabs[data-door]`
     (the strips are siblings of the sheet and inherit nothing from it). What
     the sheet actually draws at is `--sk-win-cw` / `--sk-win-ch`, derived
     on the sheet and on the strips from the ask and the picture, so no window
     can be wider than the phone it opens on. Measured 2026-09-06: the Bag
     asked 520, the Arena 566, the Character window 480, on a 390px phone, and
     two of them put their close ribbon off-screen. The clamp used to live in
     the default above, which is exactly why a per-door override of the
     default lost it. */

  /* Two more knobs the layout lab drives (world-layout-lab.js). They are
     declared here rather than inline at their use site so there is one place
     to read the current design off, and so the lab can report a real computed
     value instead of a default it invented. */
  /* A NAV RIBBON AT REST, AS A SHARE OF THE RAIL -- not a constant.
     ────────────────────────────────────────────────────────────────
     Wray, 2026-08-30, comparing prod against localhost: "the left pane
     ribbons dont touch the 3d window aligned like in localhost".

     They were 270px flat while the rail's width is `--fx`, the letterbox
     margin, which is derived from the picture. The picture is a WHOLE
     multiple of 320x240, so the rail changes in big steps and a fixed
     ribbon only lines up at one of them:

       scale 3 -> picture 960, rail ~275, a 270 ribbon touches
       scale 2 -> picture 640, rail ~435, a 270 ribbon leaves a 165px hole

     Two full-screen windows can sit on different steps, which is why the
     same build looked aligned on one machine and short on the other. A
     percentage tracks the rail at every scale, so the ribbons reach the
     picture whatever the letterbox does.

     Still a variable, and the editor's `ribbon length` slider still writes
     a px value over it -- an override is a deliberate act, a default that
     only works at one resolution is a bug.

     AT REST THEY NOW TOUCH. Wray, 2026-09-03: "On the left rail, the
     navigation ribbons. I want them to extend to touch the edge of the three
     d frame when they're in neutral position unselected. The selected ribbon
     should extend slightly onto the frame when it's selected."

     So rest is 100% -- the strip's right edge is the picture's left edge --
     and the extension that used to be the difference between shut and open is
     now an overhang PAST it, onto the frame's upright. The gesture is the
     same size it always was; it just starts from flush instead of from 30px
     short. */
  --sk-tab-w: 100%;

  /* HOW FAR THE OPEN RIBBON RUNS ONTO THE FRAME.
     ────────────────────────────────────────────
     The frame is two uprights, `--sk-frame-w` (12px) each, drawn at
     `left: var(--fx)` -- so they sit on the picture's outermost 12 columns,
     which the frame's own note calls "twelve pixels of sky". 8 covers two
     thirds of that upright and deliberately leaves a sliver of it showing:
     at the full 12 the ribbon buries the frame and the gesture reads as the
     ribbon being longer, not as it lying ON something. */
  --sk-tab-over: 8px;
  /* MUCH LARGER. Wray, 2026-09-03: "The minimap needs to be much larger".
     200 is most of the rail's 216px of usable width, up from 120 -- the slab
     is 2:1, so this takes it from 120x74 to 200x124 and the dots stop being
     three pixels lost in a thumbnail. */
  --sk-map: 320px;                /* the minimap slab, across */
  --sk-frame-w: 12px;             /* each upright beside the picture */
  /* ⭐WHOLE MULTIPLES OF THE ART (Wray, 2026-09-07: the bag's data reads
     "cramped or not in style with the look we want").

     Item icons are 16x16. 40 drew them at 2.5x and 46 at 2.875x -- both
     fractional, so every icon was resampled onto a half-pixel grid and the
     chunky edge the whole look rests on went soft. That is the #2206 trap
     one layer below the window frames it was first found on. 48 is 3x and
     64 is 4x, exact.

     ⛔THIS ONLY FITS BECAUSE THE SECOND PARCHMENT WENT. `.ww-bagwrap` used to
     draw paper.png as a 32px border inside a paper window, which left the
     grid 348 of its 412 and capped the cell at (340 - 21) / 8 = 39.8px --
     so 40 was the ceiling and the eighth column was clipped by a pixel.
     Removing it (Wray: "parchment within parchment") gives the grid the full
     412, and 8 x 48 + 7 x 3 of gap = 405 fits with 7px of slack. Bigger
     cells rather than fewer of them: BAG_COLS stays 8, which was his own
     ruling in #2591, "need to see more slots in the bag". */
  --sk-slot: 64px;                /* an equip or spell slot, ACROSS: 4x of 16 */
  /* ⭐64, was 48 (Wray, 2026-09-08: "i think 6 items wide like albion is
     better for the bag"). Six columns of 48 left 114px of slack in a 520px
     window and read as a small grid in a big page; at 64 the six fill it --
     6 x 64 + 5 x 3 = 399 in the 417 the grid gets. 4x of 16 rather than a
     fitted number, because the sprite is pixel art and a fractional cell
     blurs its dashes. */
  --sk-cell: 64px;                /* a bag cell, ACROSS: 4x of 16 */
  /* SQUARE, ruled 2026-08-30. The card-shaped slot came from an idea worth
     testing -- scale the finished bitmap cards down and use them as the icons
     -- and Wray called it after seeing it: "i had an idea to basically scale
     down the actual bit map cards when they are finished to be icons but i
     think that just wont work, we should go back to squares, its easer."

     He is right, and the reason is worth keeping: a card shrunk to 46px is a
     card nobody can read, so the shape only ever paid off if the art inside
     it was card art. It was not, so the shape was cost with no return.

     Kept as a variable, with the `slot shape` slider still in the editor,
     because the ruling was about the ART at that size rather than the shape
     in principle. 1 is square. */
  --sk-card-ar: 1;
  --sk-hole: #0b0f16;             /* the bottom of an empty slot */
}

/* ── the rails ──────────────────────────────────────────────────────── */

body.skin-majesty .ws-rail {
  background: var(--sk-ground);
  gap: 10px;
  padding: 12px;
}

/* THE RAILS COME OFF THE PICTURE'S RECTANGLE. Wray, 2026-08-29: "the side bar
   doesnt have to be on the same rail size as the scene viewer, it can go as
   far down as what fits on screen".

   world-shell.css pins them to `top: var(--fy); height: var(--fh)` -- the
   letterbox gutters beside the picture, exactly. That is why the left rail was
   480px tall on a 637px window: the picture is 640x480 because x2 is the
   largest WHOLE multiple of 320x240 that fits, and x3 (720) does not. The
   picture must not stretch to close that gap, so the rails stop matching it
   instead.

   This is layout and the skin is supposed to only paint, so it is worth being
   explicit that it is safe: applyFit() re-reads the rail's own
   getBoundingClientRect() and re-plans the column count, tile height and
   portrait size from it. A taller rail is simply a taller rail to the shell,
   and the bars Wray wants are what it chooses when there is room for them. */
/* THE OVERHANG LANE. An open ribbon has to draw PAST the rail's right edge,
   and it cannot: `overflow-y: auto` below forces `overflow-x` to compute to
   `auto` as well (one axis scrollable makes the other stop being `visible`),
   so the rail clips at exactly the line the ribbon needs to cross. Measured
   on a 1440x900 window: --fx 240, rail 240 wide, clipping at 240.

   So the rail's BOX grows by the overhang while its CONTENT BOX does not --
   the extra width is cancelled by the same amount of right padding, so the
   hero card, the wallet and everything else sit exactly where they did. Only
   the ribbon strip, which already overhangs the padding by -12px, can reach
   into the new lane.

   THE LEATHER STOPS AT THE PICTURE. The ground is a hard-stopped gradient
   rather than a flat fill, or the rail would paint 8px of leather over the
   frame and the overhang would be invisible on its own background.

   THE LANE DOES NOT EAT CLICKS. Those 8 columns are over the render, and
   click-to-move is the world's main verb, so a strip of dead picture down the
   left edge would be a real regression for a purely cosmetic change. The rail
   stops hit-testing itself and hands that job to a ::before sized to the
   leather, so the lane passes clicks through to the canvas while every
   control in the rail still takes its own. */
/* `--ws-rail-w` is the gutter by default and a chrome-pixel width under full
   bleed — see world-shell.css, which defines it. This file used to read
   `--fx` directly in all three places below, so under full bleed the rail was
   8px of pure overhang lane with the leather painted nowhere (#2507). */
body.skin-majesty .ws-rail[data-edge='left'] {
  width: calc(var(--ws-rail-w, var(--fx, 240px)) + var(--sk-tab-over, 8px));
  padding-right: calc(12px + var(--sk-tab-over, 8px));
  background: linear-gradient(90deg,
    var(--sk-ground) 0 var(--ws-rail-w, var(--fx, 240px)),
    rgba(0, 0, 0, 0) var(--ws-rail-w, var(--fx, 240px)));
  pointer-events: none;
}
/* ── UNDER FULL BLEED THE LEATHER IS A SCRIM, NOT A SLAB (#2507) ──────
   `--sk-ground` is opaque (#0a0e13) and that was free while it sat on the
   letterbox's black gutter: it painted over nothing. Under full bleed it
   paints over the GAME — a black bar the rail's width down the left (288px
   at 1920 when this was written; 192 since the desktop went to x2, #3876),
   which is the gutter back again with art on it, and the exact thing full
   bleed was meant to end.

   The ribbons carry their own drawn backgrounds (tab-on.png / tab-off.png)
   and the hero plate carries its own frame, so the slab behind them was
   never load-bearing. What it does earn is contrast: bitmap type over a
   moving 3D scene needs something behind it. So it becomes a gradient that
   is near-opaque at the screen edge and gone by the rail's inner edge —
   the type stays readable, the world stays visible, and the picture is
   uninterrupted for the two thirds of the rail's width that hold nothing.

   THIS IS THE HALF THAT IS A TASTE CALL, and it is the one dial:
   `--sk-fill-scrim` is how dark the edge is. Everything else about full
   bleed is arithmetic. */
body.skin-majesty.frame-fill-desktop .ws-rail[data-edge='left'] {
  background: linear-gradient(90deg,
    rgba(10, 14, 19, var(--sk-fill-scrim, 0.82)) 0,
    rgba(10, 14, 19, calc(var(--sk-fill-scrim, 0.82) * 0.55)) 55%,
    rgba(10, 14, 19, 0) 100%);
}

body.skin-majesty .ws-rail[data-edge='left'] > * { pointer-events: auto; }
body.skin-majesty .ws-rail[data-edge='left']::before {
  content: '';
  position: absolute;
  inset: 0 var(--sk-tab-over, 8px) 0 0;   /* the leather, not the lane */
  z-index: -1;
  pointer-events: auto;
}

body.skin-majesty .ws-rail[data-edge='left'],
body.skin-majesty .ws-rail[data-edge='right'] {
  top: 0;
  /* the rails run past the tray, because the tray no longer crosses them */
  height: calc(100vh - var(--sk-foot));
  /* A SHORT WINDOW SCROLLS RATHER THAN LOSING RIBBONS. Measured at 1280x560:
     the usable height is 417, the rail gets 290, and the card plus the ribbon
     strip need 332 -- so the bottom ribbon is off the rail before the hero
     panel is opened at all. That is not the panel's doing and no amount of
     budgeting inside it can fix it, because there is nothing left to budget.
     Scrolling makes a too-short window degrade into a scroll instead of
     silently eating the last door. */
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* the scrollbar is chrome the skin does not want drawn over its art */
body.skin-majesty .ws-rail[data-edge='left']::-webkit-scrollbar,
body.skin-majesty .ws-rail[data-edge='right']::-webkit-scrollbar {
  width: 0;
  height: 0;
}

body.skin-majesty .ws-rail[data-edge='right'] {
  /* the right side paints no ground of its own -- world-shell.css says so and
     it stays true, or the notices would sit on a slab over the picture */
  background: transparent;
}

body.skin-majesty .ws-note {
  border-style: solid;
  border-image-repeat: repeat;
  image-rendering: pixelated;
  background: transparent;
  box-shadow: none;
  color: var(--sk-ink);
}

body.skin-majesty .ws-note {
  border-image-source: url('/assets/ui/panel-well.png?v=2026-08-29a');
  border-image-slice: 4 fill;
  border-image-width: 16px;
  border-width: 16px;
  padding: 8px 12px;
}

/* The tome face goes on NAMES AND HEADINGS ONLY. Body copy stays VT323,
   which is drawn to be read in a paragraph; Blackletter is not, and a
   panel set entirely in it is a poster rather than a UI. */
/* The rail is the container, so the heading can answer to the rail's width
   rather than the window's -- the rail is a fraction of the window and the
   two do not move together. */
body.skin-majesty .ws-rail { container-type: inline-size; }

body.skin-majesty .ws-note-head {
  font-family: var(--sk-tome);
  /* 16 BY DEFAULT, 32 only when the rail can hold it. A pixel face has no
     in-between: 16px art may be shown at 16, 32 or 48 and nothing else, so
     this steps rather than scales. At 32px "· OBJECTIVES ·" measured 228px
     inside a 190px box on a 270px rail, which is the overflow Wray saw. */
  font-size: 16px;
  line-height: 1.1;
  letter-spacing: 0;
  color: var(--sk-gold-hi);
  text-shadow: 0 2px 0 #000;
  text-transform: none;
}

@container (min-width: 340px) {
  body.skin-majesty .ws-note-head { font-size: 32px; }
}

body.skin-majesty .ws-note-line,
body.skin-majesty .ws-quest,
body.skin-majesty .ws-quest-name,
body.skin-majesty .ws-quest-desc {
  color: var(--sk-ink);
  text-shadow: none;
}

/* the objectives tickbox: a CHECKBOX, not a panel. It picked up the panel
   rule while .ws-box was in that list and rendered as an empty gold-rimmed
   well the size of a card. */
body.skin-majesty .ws-box {
  border: 3px solid var(--sk-gold);
  background: #141a22;
  box-shadow: none;
  width: 20px;
  height: 20px;
  border-radius: 0;
}
body.skin-majesty .ws-box[data-done='1'] { background: var(--sk-gold-hi); }

/* ── the doors ──────────────────────────────────────────────────────── */

/* BARS, NOT A GRID. Wray, 2026-08-29: "i think i prefer the bars as i already
   know of some missing windows that should live there."

   Forcing one column is what pushed the sixth door off the rail last time, so
   the rule that makes it safe this time is arithmetic, not hope: the shell
   budgets `rows * h` for the pad, and on the now-full-height rail its own plan
   is 3 rows of 118 = 354px. Six bars at 44 with 8px gaps is 304. SHRINKING the
   pad below what fitRail set aside is safe; growing it is what broke.

   At one column the tile is 376px wide, so the drawn 9-slice fits with 336px
   of face left — the thing flat mode exists to avoid does not apply here, and
   the border is restored even though the shell still says data-tile="flat"
   (it decided that from a grid tile's width, which is no longer the width). */
body.skin-majesty .ws-pad {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* THE DOORS SCROLL RATHER THAN OVERFLOW. Measured before the spellbook was
     added: rail 587, stack 553, spare -10. A seventh bar is 46px that is not
     there at this window height, and the honest options were to drop a door,
     shrink the type Wray just asked to enlarge, or let the list scroll. It
     scrolls: at 900px every door is visible without one, and at 780 the last
     is a flick away rather than gone. */
  overflow-y: auto;
  min-height: 0;
  scrollbar-width: none;
}
body.skin-majesty .ws-pad::-webkit-scrollbar { width: 0; }

/* A HALF-CUT BAR READS AS BROKEN, NOT AS SCROLLABLE. At 780px of window there
   is 31px less room than seven doors, a plate, a wallet chip and a way out
   need, so the list scrolls — and a mask makes the cut edge say so instead of
   looking like a rendering fault. The alternative is cutting a door, which is
   Wray;s call rather than mine. */
body.skin-majesty .ws-pad {
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 18px), transparent 100%);
  mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 18px), transparent 100%);
}
/* the tabbed rail does not scroll: all seven seat, so the fade would be
   promising more below when there is none */
body.skin-majesty .ws-rail.wrt-on .ws-pad {
  overflow: visible;
  -webkit-mask-image: none;
  mask-image: none;
}

/* the wallet keeps its place above the doors (Wray, 2026-08-26: "the balance
   goes above where the menu buttons start") but not its full height */
body.skin-majesty .ws-chip { height: 40px; }

/* ── LOG OUT ────────────────────────────────────────────────────────────
   Outside the scrolling pad, at the foot of the rail: the way out of the
   world must not be the thing that scrolled off the bottom of it. Muted, and
   red only on approach — it is a door you should be able to find and should
   not press by accident. */
body.skin-majesty .ws-logout {
  flex: 0 0 auto;
  margin-top: 8px;
  height: 34px;
  border: 3px solid #3b4553;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  color: var(--sk-ink-dim);
  text-shadow: none;
  font-size: 8px;
  letter-spacing: .06em;
  cursor: pointer;
}
body.skin-majesty .ws-logout:hover {
  border-color: var(--sk-hot);
  color: #ffd9d4;
  background: rgba(224, 60, 49, .18);
}
body.skin-majesty .ws-logout:disabled { opacity: .5; cursor: default; }

body.skin-majesty .ws-padwrap {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

body.skin-majesty .ws-rail .ws-tile {
  height: 44px;
  min-height: 0;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  text-align: left;
  white-space: nowrap;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: 12px 16px 16px 16px;
  border-width: 12px 16px 16px 16px;
  background: transparent;
  box-shadow: none;
  padding: 0 6px;
}
body.skin-majesty .ws-rail .ws-tile:hover,
body.skin-majesty .ws-rail .ws-tile[aria-current='page'] {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
  background: transparent;
  color: var(--sk-ink-on-gold);
}
body.skin-majesty .ws-rail .ws-tile:active {
  border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a');
  transform: none;
}

/* THE SKIN DOES NOT LAY THE PAD OUT. It used to force one column here, which
   is where "too big, overflowing from the bottom rail" came from: world-shell.js
   runs fitRail(railW, railH) and picks the column count AND the tile height for
   the rail it actually got, then budgets `rows * h` against the space left. A
   400x480 rail is planned as a grid; one column of six is two to three times
   taller than that budget, so the sixth door went off the bottom and under the
   chat bar. The shell owns arrangement, this file owns paint. */

/* THE DRAWN TILE, only where a border fits. world-shell.css sets
   data-tile="flat" when the rail is too narrow to spend a 9-slice on, and its
   reasoning is measured and correct: at 119px wide a 20px border each side is
   a third of the tile and the label wraps to two lines. So the drawn button is
   scoped to the wide rail, and the narrow one gets the same palette with a
   plain border below. Both are navy and gold; only the frame differs. */
body.skin-majesty .ws-rail:not([data-tile='flat']) .ws-tile {
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  /* asymmetric on purpose: the art's top edge is 4px and its shaded bottom
     is 5, so a uniform slice would smear the shadow up over the face */
  border-image-slice: 4 5 5 5 fill;
  border-image-width: 16px 20px 20px 20px;
  border-width: 16px 20px 20px 20px;
  image-rendering: pixelated;
  background: transparent;
  box-shadow: none;
  /* padding only, and less than the shell's, so a skinned tile is never
     TALLER than the one fitRail budgeted for. Shrinking is safe; growing is
     what pushed a door off the rail. No height, no flex-direction, no gap:
     those are the shell's. */
  padding: 4px 6px;
  color: var(--sk-ink);
  text-shadow: none;
}

body.skin-majesty .ws-rail:not([data-tile='flat']) .ws-tile:hover,
body.skin-majesty .ws-rail:not([data-tile='flat']) .ws-tile[aria-current='page'] {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
  color: var(--sk-ink-on-gold);
}
body.skin-majesty .ws-rail:not([data-tile='flat']) .ws-tile:active {
  border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a');
}

/* The flat-tile block that used to sit here is GONE. It existed because a
   119px grid tile could not carry a 20px border, and the pad is one column of
   376px bars now, so the case it handled cannot occur. It was also out-
   specifying the bar rule above it at (0,4,1) against (0,3,1), which is why
   the bars kept rendering with a plain 4px border and no art. */

/* THE REAL DOOR ICONS. world-shell.css calls its six inline glyphs
   "stand-ins ... whoever draws the real door icons replaces these six rules
   and nothing else", so this replaces exactly those six. Clockwork Raven's
   menu set, in an 8-wide strip with the colour row over the grey row: a tile
   shifts background-position-y rather than swapping an image. */
/* KEYED ON THE ICON, NOT ON THE RAIL. Rooms and Settings now live under the
   minimap, which is not inside `.ws-rail`, so a `.ws-rail .ws-ico` selector
   stopped matching them and they rendered as two blank plates. The glyph
   belongs to the door wherever the door is put. */
body.skin-majesty .ws-ico {
  /* 16, NOT 32. fitRail sizes the bar at 40px and the drawn border takes 24 of
     it, so the content box is 16px tall — a 32px glyph hung out of the top and
     bottom of its own button and read as sitting ON the bar rather than in it.
     16 is the art's native size, so it is still a whole multiple and still
     crisp; it is simply the largest one that fits a bar this tall. */
  width: 16px;
  height: 16px;
  background-image: url('/assets/ui/nav-icons.png?v=2026-09-06b');
  /* TEN COLUMNS SINCE 2026-09-06, not eight. Character and Guild have a
     glyph of their own now (see below); the first eight are byte-identical,
     so every offset under this line still points at the art it always did. */
  background-size: 160px 32px;
  background-position-y: -16px;      /* the grey row */
  background-repeat: no-repeat;
}
body.skin-majesty .ws-tile:hover .ws-ico,
body.skin-majesty .ws-tile[aria-current='page'] .ws-ico {
  background-position-y: 0;          /* the colour row */
}
/* data-door carries the KIND, which did not change when the labels did, so
   these keys still read `stall` and `record`. */
body.skin-majesty .ws-ico[data-door='rooms']     { background-position-x: 0; }
body.skin-majesty .ws-ico[data-door='arena']     { background-position-x: -16px; }
body.skin-majesty .ws-ico[data-door='board']     { background-position-x: -32px; }
body.skin-majesty .ws-ico[data-door='stall']     { background-position-x: -48px; }
body.skin-majesty .ws-ico[data-door='nine']      { background-position-x: -64px; }
body.skin-majesty .ws-ico[data-door='record']    { background-position-x: -80px; }
body.skin-majesty .ws-ico[data-door='spellbook'] { background-position-x: -96px; }
/* ⭐COLUMN 7 IS THE RETIRED QUESTION MARK. Character wore it, and Guild
   borrowed the leaderboard's cup, because the sheet was eight columns and
   both doors arrived after it was drawn. Wray, 2026-09-06: "the cosy menu
   button icons need changing they dont really represent where they go" --
   the row read as a "?", two identical trophies, and six things. Both now
   have their own glyph, from the same pack and the same two rows the other
   eight were cut from (a knight's helmet, a hall with a red roof). The "?"
   stays in the sheet unused: cropping it would move all seven after it. */
body.skin-majesty .ws-ico[data-door='character'] { background-position-x: -128px; }
body.skin-majesty .ws-ico[data-door='guild']     { background-position-x: -144px; }

/* ── the wallet chip ────────────────────────────────────────────────── */

body.skin-majesty .ws-chip {
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: 16px 20px 20px 20px;
  border-width: 16px 20px 20px 20px;
  image-rendering: pixelated;
  background: transparent;
  box-shadow: none;
  height: 52px;
  padding: 0 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 8px;
  color: var(--sk-ink);
  text-shadow: none;
}
body.skin-majesty .ws-chip:hover {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
  color: var(--sk-ink-on-gold);
}

/* ── the notices, right side ────────────────────────────────────────── */

body.skin-majesty .ws-note .ws-chip {
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-bright.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: 16px 20px 20px 20px;
  border-width: 16px 20px 20px 20px;
  image-rendering: pixelated;
  background: transparent;
  box-shadow: none;
  height: 52px;
  padding: 0 6px;
  color: var(--sk-ink-on-gold);
  text-shadow: none;
  font-size: 8px;
}

/* the quest meter is Majesty's own segmented bar, which is drawn to hold
   discrete steps rather than a percentage -- so it is the right art for a
   "3 of 9 rounds" reading and the wrong art for a loading bar.

   ⭐THE FRAME IS AN OVERLAY (#2184). meter.png is line art, not a filled
   bar: 383 of its pixels are fully transparent where the eight sockets are,
   and 160 sit at quarter alpha as the highlight it casts over whatever is
   beneath it. Drawn as the element's background it went UNDER the fill, so
   every lit segment painted straight over its own socket ring. Same shape
   the loading screen's #ld-meter took in #2183: the fill is the child, the
   frame sits on top in ::after, and __tests__/worldSkinMeter.test.js reads
   the offsets below out of the PNG rather than trusting them. */
body.skin-majesty .ws-meter {
  background: none;
  border: 0;
  box-shadow: none;
  image-rendering: pixelated;
  height: 28px;
  position: relative;
  overflow: hidden;
}
body.skin-majesty .ws-meter::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: url('/assets/ui/meter.png?v=2026-09-11a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
}
body.skin-majesty .ws-meter > * {
  position: absolute;
  /* 4, not 8: the sockets start at art x2 and the fill lozenge sits at art
     y2, so at the x2 the bar is drawn at both offsets are 4px. At 8 the fill
     sat low in every socket with a band of bare ground above it. */
  left: 4px;
  top: 4px;
  height: 12px;
  /* never past the last socket's right edge, whatever percentage is set */
  max-width: calc(100% - 8px);
  background: url('/assets/ui/meter-fill.png?v=2026-09-11a') 0 0 / auto 100% repeat-x;
  image-rendering: pixelated;
  border-radius: 0;
}

body.skin-majesty .ws-tabs {
  color: var(--sk-ink);
}

@media (max-width: 900px) {
  /* the rail collapses on a phone and world-shell.css owns how; nothing here
     may fight it, so only the palette survives the breakpoint */
  body.skin-majesty .ws-pad { gap: 6px; }
  body.skin-majesty .ws-rail .ws-tile { height: 48px; }
}


/* ── the two pieces that inject their own <style> at runtime ─────────
   world-camera-snap.js and world-presence.js each append a <style> to
   <head>, which lands AFTER this file. Order would beat us at equal
   specificity, so every rule below carries the body class and, where the
   other sheet uses one, the id -- that is what wins, not !important. */

/* THE CAMERA PAD MOVES LEFT. Wray: "move the camera buttons so its not being
   covered" — the pad and the target both sat in the picture's bottom-right,
   and the target is the one that has to be readable. */
body.skin-majesty .wcs-pad {
  right: auto;
  left: 14px;
}

/* The frame, the art and the four states all come from THE MAJESTY PLANK
   rule at the foot of this file now (Wray, 2026-09-06). This block used to
   carry its own copy of them with the frame hand-set at 16/20/20/20 -- a 4x
   upscale of a 4/5 slice while the world drew at 2x, which is exactly the
   "chunky" Wray was pointing at. Only the glyph size is the pad's own. */
body.skin-majesty .wcs-pad .wr-plank {
  font-size: 14px;
  padding: 0 6px 3px;
}

/* Wray, 2026-08-29: "the bottom text tray needs to sit as a thin thin bar
   right at the bottom extreme". Pinned rather than left in flow, where it sat
   57px clear of the bottom with the picture ending 49px above it. */
body.skin-majesty #world-chat {
  /* world-presence.js positions this itself, and cleverly: centred on the
     PICTURE via `left: calc(var(--fx) + var(--fw)/2)` with
     `transform: translateX(-50%)`, and vertically centred in the letterbox
     band under it. Overriding `left` alone left the transform and the
     `width: min(640px, ...)` in place, which threw the bar half its own width
     off the left edge. Every property in that set has to be answered. */
  /* UP TO MEET THE PICTURE. Wray: "move chat up to meet the frame and the
     very bottom extreme hosts something else". The tray sat at the window's
     bottom with ~110px of dead letterbox between it and the render, because
     the picture can only be a whole multiple of 320x240 and x2 is all that
     fits. Anchored to the render's own bottom edge the gap closes, and what
     is left below becomes a strip of its own. */
  /* THE TRAY SPANS THE PICTURE, NOT THE WINDOW. Full width, it forced the
     rails to end at the picture's bottom edge to avoid it, which cut the door
     list in half. Anchored to the render rect it sits under the scene and
     leaves both gutters to the rails, which is also where it belongs: it is
     the room's chat, not the shell's. */
  position: fixed;
  left: var(--fx, 0px);
  width: var(--fw, 100vw);
  right: auto;
  top: calc(var(--fy, 0px) + var(--fh, 100vh));
  bottom: auto;
  transform: none;
  /* NO `width: auto` HERE. It was left behind from the edit that undid
     world-presence.js's `width: min(640px, ...)`, three lines after the
     `width: var(--fw)` above, and being later it won: the tray shrank to fit
     its buttons (328px) inside a 640px picture and looked like the input had
     stopped growing. */
  max-width: none;
  margin: 0;
  /* NO GAP AND NO INSET. Wray: "needs to stretch the whole width of the 3d
     window and no gap between." The side padding was holding the input off
     the picture's edges. */
  padding: 5px 0;
  gap: 6px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  background: var(--sk-ground);
  border-top: 4px solid var(--sk-gold);
  height: var(--sk-tray);
  z-index: 60;
}

/* THE TRAY'S CONTROLS ARE FLAT, and for the same reason the narrow tiles are.
   A 9-slice here is 16px of border on top and 20 on the bottom; on the 36px
   control a thin bar can afford that is 36px of border and ZERO content
   height, so every label was clipped to nothing. It is the identical failure
   this whole arc started with -- Medieval Majesty's menu button needing 80px
   and holding no text at 64 -- reproduced in my own stylesheet. Below a
   certain height the drawn frame is not an option, and pretending otherwise
   just hides the text. */
/* THE TRAY IS BITMAP TOO. world-presence.js sets `font: inherit` on these,
   which inherits the PAGE's serif — Wray: "chat at the bottom of the screen
   is not in our uniform bitmap font". */
body.skin-majesty #world-chat,
body.skin-majesty #world-chat input,
body.skin-majesty #world-chat button {
  font-family: var(--sk-body, 'VT323', monospace) !important;
}
body.skin-majesty #world-chat .wp-verb,
body.skin-majesty #world-chat button[type='submit'] {
  font-family: var(--sk-head, 'Press Start 2P', monospace) !important;
  font-size: 8px;
  letter-spacing: .04em;
  opacity: 1;
  height: 40px;
  padding: 0 14px;
  /* the plate itself is below, with the rest of the Cozy buttons */
}

body.skin-majesty #world-chat input {
  /* `flex: 1 1 auto` sizes from content and left a gap; `1 1 0` makes the
     input take everything the buttons do not */
  flex: 1 1 0;
  min-width: 0;
  max-width: none;
  font-size: 20px;
  height: 40px;
  padding: 0 12px;
  color: var(--sk-ink);
}
body.skin-majesty #world-chat input:focus { border-color: var(--sk-gold); }
body.skin-majesty #world-chat input::placeholder { color: var(--sk-ink-dim); }


/* ══════════════════════════════════════════════════════════════════════
   THE RIGHT RAIL'S THREE PANELS (world-target.js)
   Map at the top, Nerm under it, the target stage at the bottom. Wray asked
   for the map top-right and the stage bottom-right, and for Nerm not to be
   the thing in the corner.

   The rail scrolls: the daily pack and objectives are still below these
   three, and folding those two under one ribbon-tabbed window is the next
   job. Until then a scroll loses nothing, where clipping would.
   ══════════════════════════════════════════════════════════════════════ */

/* the notices sit BELOW the three panels, which publish their height as
   --wt-h after every layout; one owner, so the two cannot overlap */
body.skin-majesty .ws-rail[data-edge='right'] {
  top: var(--wt-h, 0px);
  height: calc(100vh - var(--sk-tray) - var(--wt-h, 0px));
  overflow-y: auto;
  scrollbar-width: none;
}

body.skin-majesty #wt-rail::-webkit-scrollbar { width: 0; }
body.skin-majesty #wt-rail {
  position: fixed;
  right: 0;
  top: 0;
  width: var(--fx, 320px);
  padding: 12px;
  /* LEAVE ROOM FOR WHAT IS UNDER IT. The three panels measure ~720px in a
     637px window, and publishing that as --wt-h pushed the notices rail --
     with the daily pack's CLAIM button on it -- clean off the screen. Nothing
     may become unreachable to make room for something else, so the column
     caps itself and scrolls, and the published height is the capped one
     because it is read from the same box. Folding the notices into a
     ribbon-tabbed window is what actually fixes this; this is the interim
     that loses nothing in the meantime. */
  /* the notices no longer need reserved space below: they are pages inside
     the journal now, so the column gets the whole height and Nerm stops
     being clipped off the bottom of it */
  /* stops above the pinned target rather than running under it */
  max-height: calc(100vh - var(--sk-foot) - var(--sk-target) - 24px);
  overflow-y: auto;
  scrollbar-width: none;
  z-index: 39;                 /* over the render, under the rail's own chrome */
  box-sizing: border-box;
  pointer-events: auto;
}
body.skin-majesty .ws-rail[data-edge='right']::-webkit-scrollbar { width: 0; }

body.skin-majesty .wt-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

body.skin-majesty .wt-panel {
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/panel-well.png?v=2026-08-29a');
  border-image-slice: 4 fill;
  border-image-width: 16px;
  border-width: 16px;
  padding: 6px 10px;
  image-rendering: pixelated;
  color: var(--sk-ink);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* 32, NOT 16. Wray: "the player name and the window titles on the right rail
   is not big enough for the font to pay off." A blackletter at 16px is a small
   serif and reads as a mistake; the whole reason for the face is the size. The
   rail is 400px wide now, so it fits. */
body.skin-majesty .wt-head {
  font-family: var(--sk-tome);
  font-size: 32px;
  line-height: 1.1;
  color: var(--sk-gold-hi);
  text-shadow: 0 2px 0 #000;
}

/* ── the journal: one window, ribbons for tabs ──────────────────────── */

body.skin-majesty .wt-journal { position: relative; padding-top: 0; }

/* THE JOURNAL'S RIBBONS ARE THE WINDOW'S RIBBONS. Same art, same two states,
   same nine-slice, same expand — Wray's canon: "#A4343A should be the canon
   colour for unopened ribbons, the gold can be the highlighted". Two ribbons
   that differ by panel would be two ribbons. */
body.skin-majesty .wt-marks {
  display: flex;
  gap: 8px;
  padding-left: 10px;
  margin: -14px 0 -34px;      /* hang from the panel's own top edge */
  height: 40px;
  position: relative;
  z-index: 3;
  align-items: flex-start;
}

body.skin-majesty .wt-mark {
  width: 30px;
  height: 62px;
  border-style: solid;
  border-image-source: url('/assets/ui/ribbon-closed.png?v=2026-08-29b');
  /* `fill` IS REQUIRED HERE. With left/right slices of 0 the corner regions
     are zero-width and only the top cap and bottom notch draw; the ribbon's
     repeating MIDDLE is the centre region, which is not painted without it.
     Left out, the ribbons rendered as two slivers. */
  border-image-slice: 5 0 4 0 fill;
  border-image-width: 10px 0 8px 0;
  border-width: 10px 0 8px 0;
  border-image-repeat: repeat;
  background: none;
  box-shadow: none;
  border-radius: 0;
  image-rendering: pixelated;
  cursor: pointer;
  padding: 0 0 10px;
  display: flex;
  justify-content: center;
  align-items: center;       /* the label sits in the field, not on the notch */
  transition: height 140ms steps(4, end);
}
body.skin-majesty .wt-mark:hover { height: 74px; }
body.skin-majesty .wt-mark:not(.off) {
  border-image-source: url('/assets/ui/ribbon-open.png?v=2026-08-29b');
  height: 86px;
}
body.skin-majesty .wt-mark i {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  font-style: normal;
  color: #f6ecd6;
  writing-mode: vertical-rl;
  letter-spacing: .06em;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
}
body.skin-majesty .wt-mark:not(.off) i { color: #2e2113; text-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  body.skin-majesty .wt-mark { transition: none; }
}

body.skin-majesty .wt-pages {
  /* clears the open ribbon, which is 86px hanging from -14: 72px into the
     page. Set to 4 when the scroll cap went in, and the first quest went
     straight behind the QUESTS marker. */
  padding-top: 76px;
  max-height: 250px;
  overflow-y: auto;
  scrollbar-width: none;
}
body.skin-majesty .wt-pages::-webkit-scrollbar { width: 0; }
body.skin-majesty .wt-pages [hidden] { display: none; }

/* the adopted notices lose their own frame: they are pages in this window
   now, not panels in a rail */
body.skin-majesty .wt-pages .ws-note {
  border: 0 !important;
  padding: 0;
  background: none;
}
body.skin-majesty .wt-pages .ws-note-head { display: none; }

/* THE ADOPTED NOTICES BRING THEIR OWN TYPE. world-shell.css sets these from
   --wc-body, which resolves to the page's serif, so the quest names came
   through in Crimson Text next to panels set in VT323 — Wray: "the quest and
   the news box especially has a more updated font". They are pages in this
   window now, so they take this window's type. */
body.skin-majesty .wt-pages,
body.skin-majesty .wt-pages .ws-note-line,
body.skin-majesty .wt-pages .ws-quest,
body.skin-majesty .wt-pages .ws-quest-name,
body.skin-majesty .wt-pages .ws-room-sub {
  font-family: var(--sk-body, 'VT323', monospace) !important;
  font-size: 20px;
  line-height: 1.3;
  color: var(--sk-ink);
  text-shadow: none;
}

/* the instruction line, inside the journal. Same face, smaller and quieter:
   the objectives list has to stay scannable as three things, not six. */
body.skin-majesty .wt-pages .ws-quest-desc {
  font-family: var(--sk-body, 'VT323', monospace) !important;
  font-size: 17px;
  line-height: 1.2;
  color: var(--sk-ink);
  opacity: .62;
  text-shadow: none;
}
body.skin-majesty .wt-pages .ws-chip {
  font-family: var(--sk-head, 'Press Start 2P', monospace) !important;
  font-size: 8px;
}

/* ── THE INBOX PAGE (#2514) ──────────────────────────────────────────────
   A line of type that happens to be pressable, not a card: this page sits in
   a 280px column beside the map, and a row of boxes there would read as a
   second notice board rather than as the journal's third page. The unread
   marker is a gold rule down the leading edge -- the same "this one is the
   live one" language the ribbons use, in the space a narrow row has. */
body.skin-majesty .wt-inrow {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-left: 3px solid transparent;
  background: none;
  cursor: pointer;
  padding: 4px 4px 4px 7px;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 19px;
  line-height: 1.25;
  color: var(--sk-ink);
  text-shadow: none;
}
/* MEASURED, NOT PICKED. The first cut used #8a5a1f, which is the rooms
   navigator's gold -- and that gold is tuned for PARCHMENT. This panel
   resolves --sk-ink to #f0e2c6, i.e. it is one of the DARK surfaces, and the
   brown came back as the least legible thing on it. Same lesson the house
   panel wrote down about brand colours on a light sheet, in the other
   direction. */
body.skin-majesty .wt-inrow b { font-weight: 400; color: #e6c88a; }
body.skin-majesty .wt-inrow:hover { background: rgba(162, 129, 85, .18); }
body.skin-majesty .wt-inrow.is-new { border-left-color: #e6c88a; }
body.skin-majesty .wt-inrow:focus-visible {
  outline: 2px solid var(--sk-gold);
  outline-offset: -2px;
}

/* ── Nerm: parchment, bitmap type, no hard frame ────────────────────── */

body.skin-majesty .wt-nerm {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  /* the parchment scrap IS the surface. No nine-slice box: "maybe he also
     doesnt need a frame hard locked". */
  background: url('/assets/ui/paper.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
  padding: 10px 12px;
  box-shadow: 0 3px 0 rgba(0, 0, 0, .5);
}
body.skin-majesty .wt-face {
  width: 48px;
  height: 48px;
  flex: 0 0 auto;
  background: url('/assets/ui/nerm.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
}
body.skin-majesty .wt-line {
  margin: 0;
  /* bitmap, on parchment, dark ink — as the mock had him */
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 21px;
  line-height: 1.2;
  color: #2e2113;
}

/* ── the target: free standing, bottom right, no frame ──────────────── */

/* PINNED TO THE FOOT OF THE RAIL, AND IT DOES NOT MOVE. Wray: "when I select
   something, the preview of it gets moved down by ... the quest or the news box
   getting larger, which shouldn't happen. That target should always be there in
   the bottom right."

   In the rail's flow it was the last item in a column, so anything above it
   growing pushed it down — a readout that moves while you read it. Taken out
   of flow and anchored to the bottom of the right gutter it is in the same
   place every time, and the journal above can grow into its own scroll
   without touching it. Still frameless: the thing you clicked stands on its
   own. */
body.skin-majesty.wt-has-target { --sk-target: 236px; }

/* THE BOX IS DECLARED ONCE, further down, with the layered layout. This used
   to be a centred flex COLUMN -- canvas, then name, then sub, then buttons --
   and leaving it here meant two rules describing the same element in two
   different layouts, with the later one winning only the properties it
   happened to name. The name stayed centred that way, because `text-align:
   center` was set here and never overridden there. */
body.skin-majesty #wt-target[hidden] { display: none; }

body.skin-majesty .wt-stagec {
  width: 100%;
  max-width: 240px;
  height: 150px;
  display: block;
  /* NO GROUND OF ITS OWN. The soft glow lived here, on the canvas -- and
     the canvas does not span the pane, so its bottom edge drew itself as a
     hard line where glow met black. Wray, 2026-09-04, third description of
     the same line: "either the box the object selected with the soft glow
     extends to the rest of the text frame or the glow is removed so there
     is no crop line between where the two meet." The glow moved to the
     PANE (#wt-target below), which the words also live on, so there is one
     ground and no meeting point. */
}
/* THE CAPITAL. Wray: "targeting a player in the target space, their name has
   a #A4343A first letter then the rest normal, probably slightly larger first
   letter too." The blackletter earns its keep as one letter rather than a
   whole line of it. */
/* THE NAME IS STYLED ONCE, under .wt-info further down. These were the
   right-aligned version from when the pane was a centred column, and leaving
   them meant the later rule only won the properties it happened to name --
   `justify-content: flex-end` was not one of them, so the name kept sitting
   to the right of a block that had been moved to the left. Second time this
   file has bitten that way in one pass; the pattern is two rules describing
   one element in two different layouts. */
body.skin-majesty #wt-target .wt-sub {
  /* the level-and-house line, up with the name (same ask) */
  font-size: 17px;
}
body.skin-majesty .wt-sub {
  font-size: 19px;
  color: var(--sk-ink);
  text-shadow: 0 2px 0 #000;
}
body.skin-majesty .wt-acts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
  width: 100%;
}
body.skin-majesty .wt-act {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  height: 34px;
  padding: 0 10px;
  /* the plate itself is below, with the rest of the Cozy buttons */
}


/* the moved wallet wrapper is just a box round the chip; the address readout
   it also holds has nowhere to go under a 120px map, so it is not painted */
body.skin-majesty .wt-tools .ws-wallet {
  display: block;
  box-sizing: border-box;
  width: 44px;
  height: 44px;
}
body.skin-majesty .wt-tools .ws-wallet[hidden] { display: none; }
body.skin-majesty .wt-tools .ws-wallet-sub { display: none; }
body.skin-majesty .wt-tools .ws-wallet .ws-chip { width: 44px; height: 44px; }

body.skin-majesty .wt-tools .wrt-plate .ws-hint { display: none; }
body.skin-majesty .wt-tools .wrt-plate .ws-ico { width: 16px; height: 16px; margin: 0; }

body.skin-majesty .wt-tools .wrt-plate,
body.skin-majesty .wt-tools .wrt-icon,
body.skin-majesty .wt-tools .ws-chip {
  /* the 20px of drawn border counts INSIDE the 44, or the button comes out
     larger than the square it was sized to be */
  box-sizing: border-box;
  /* 44px: the smallest square a thumb hits reliably, and two of them plus the
     gap is 94px, which fits under a 120px map. */
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  padding: 0;
  /* the LABEL still exists in the DOM for the accessible name; what is hidden
     is only its painting */
  font-size: 0;
  color: transparent;
  border-style: solid;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 10px;
  border-width: 10px;
  border-image-repeat: repeat;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  position: relative;
  cursor: pointer;
}
body.skin-majesty .wt-tools .wrt-icon:hover,
body.skin-majesty .wt-tools .ws-chip:hover {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
}
body.skin-majesty .wt-tools .wrt-icon:active,
body.skin-majesty .wt-tools .ws-chip:active {
  border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a');
}

/* THE GLYPHS ARE SPRITES, NOT CSS SHAPES. The first cut drew both out of
   borders and box-shadows; on screen the wallet read as a playing card and the
   way out read as a book spine. nav-icons.png has eight icons and neither of
   these is among them — the nearest were the Stall's coin pouch and a question
   mark — so they are drawn: `tool-icons.png`, two 16x16 glyphs in the same
   five-stop ramp and the same outline-plus-two-tones language as Cozy's set,
   laid out as a colour row over a grey row exactly like nav-icons.png so the
   same position trick lights them. Source: scripts note in the file header.

   Grey at rest and colour on hover, which is what the seven doors do. */
body.skin-majesty .wt-tools .wrt-icon::after,
body.skin-majesty .wt-tools .ws-chip::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 16px;
  transform: translate(-50%, -50%);
  background-image: url('/assets/ui/tool-icons.png?v=2026-08-29a');
  background-size: 32px 32px;
  background-repeat: no-repeat;
  background-position-y: -16px;      /* the grey row */
  image-rendering: pixelated;
}
body.skin-majesty .wt-tools .wrt-icon:hover::after,
body.skin-majesty .wt-tools .ws-chip:hover::after {
  background-position-y: 0;          /* the colour row */
}
body.skin-majesty .wt-tools .ws-wallet .ws-chip::after {
  background-position-x: 0;
}
body.skin-majesty .wt-tools .ws-logout::after {
  background-position-x: -16px;
}


/* ══════════════════════════════════════════════════════════════════════
   A RANKED TABLE INSIDE A WINDOW (#2020)
   ──────────────────────────────────────────────────────────────────────
   The ladder and the house table are the same four columns, so they are one
   set of rules rather than two that look alike. Rank right-aligned so the
   digits stack, name filling, note quiet, score right-aligned and tabular.

   The first cut shipped with NO rules at all and every row rendered as one
   run-on string: "#1guest-fcbb51aa42d7Ashenvale100". Four spans with no
   layout are four spans, not a table.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ww-board {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}

/* CENTRED ON THE PAGE. Wray, 2026-08-30: "the actual leaderboard data should
   be centred to the middle of the page so we can size the panel accordingly."

   A max-width plus auto margins rather than centring the TEXT: the four
   columns still line up left-to-right inside the block, which is what makes a
   ladder scannable. What centres is the block, so widening the window grows
   the parchment around the table instead of stretching the table across it. */
body.skin-majesty .ww-table {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  padding-right: 4px;
}

/* IT HAS TO FILL BEFORE IT CAN CENTRE. `.ws-sheet-body` is a flex ROW, so
   `.ww-board` was sizing to its own content -- 343px inside a 484px box --
   and `margin: 0 auto` on the table then centred it within 343 rather than
   within the page. The table looked deliberately left-aligned and measured 71
   pixels off the parchment's middle.

   `flex: 1 1 auto` plus an explicit width is the pair that fixes it: the
   first makes it take the row, the second stops min-content from winning. */
body.skin-majesty .ww-board {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  align-items: center;
}

/* ⭐THE SUBLINE SCALE (#2557, approved by Wray 2026-09-21: "the type change
   on the parchment is a great change"). The docked panels are .24 x .88 of
   the picture since #2751 and the type inside them never followed: sublines
   sat at 13px VT323 in a 938px-tall dock and read as a page's type in a
   window built for a game's. 13 becomes 18 on every parchment subline here
   (.ww-foot, .ww-note, .ww-hover > i, .wf-row-s, .ww-profnote), and the dim
   ink is #5a4328 everywhere it was #6b5232: measured against paper.png that
   is 5.6:1 where the old value was 3.9:1, under the 4.5:1 floor. */
body.skin-majesty .ww-foot {
  flex: 0 0 auto;
  margin: 6px 0 0;
  text-align: center;
  font-size: 18px;
  color: #5a4328;
}

body.skin-majesty .ww-rank {
  display: grid;
  /* rank | who | note | score. The name takes the slack; the other three are
     sized to their longest honest content so nothing jumps between rows. */
  grid-template-columns: 40px 1fr auto 72px;
  align-items: center;
  gap: 10px;
  padding: 5px 8px;
  font-size: 17px;
  line-height: 1.2;
  color: #3a2a18;
}

/* every other row banded, so a long ladder can be read across */
body.skin-majesty .ww-rank:nth-child(even) { background: rgba(58, 42, 24, .07); }

body.skin-majesty .ww-rk {
  text-align: right;
  color: #5a4328;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}

body.skin-majesty .ww-who {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* THE BLACKLETTER INITIAL, KEPT SMALL. Wray asked for a calligraphic first
   letter on a name and then said of the first attempt: "its too large to be
   legible". So it is the same size as the line, in the ribbon red, and only
   the letterform changes — the flourish is the shape, not the scale. */
body.skin-majesty .ww-cap {
  font-family: 'UnifrakturMaguntia', 'Pixelify Sans', inherit;
  color: #a4343a;
  font-weight: 400;
  font-size: 1.15em;
  line-height: 1;
}

body.skin-majesty .ww-note {
  color: #5a4328;
  font-size: 18px;
  white-space: nowrap;
}

body.skin-majesty .ww-score {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: #2e2113;
}

/* YOUR OWN LINE. The only thing anybody scans a ladder for is themselves, so
   it is marked in the ribbon red with a rule down its edge rather than by a
   background that would fight the banding above. */
body.skin-majesty .ww-rank.is-you {
  background: rgba(164, 52, 58, .12);
  box-shadow: inset 3px 0 0 0 #a4343a;
}
body.skin-majesty .ww-rank.is-you .ww-score,
body.skin-majesty .ww-rank.is-you .ww-rk { color: #a4343a; }

/* ⭐YOUR LINE STAYS IN VIEW (#3031, 2J). Held at the bottom of the list while
   it is below the fold, so the one row anybody opens a ladder for is on
   screen at once; scrolled to, it sits in its place. The pinned line (you
   are not in the 60 shown) is the list's last row, so it is always there.
   Opaque, because rows scroll UNDER a sticky one and a translucent wash lets
   them show through: the parchment's own tone (#d5b68c, sampled off the
   sheet at 1920) under the same red. The podium plates later in this file
   out-rank this background, so a top-three "you" keeps its plate. */
body.skin-majesty .ww-rank.is-you {
  position: sticky;
  bottom: 0;
  z-index: 2;
  background: linear-gradient(rgba(164, 52, 58, .12), rgba(164, 52, 58, .12)), #d5b68c;
}

/* the break before an out-of-the-top-25 line */
body.skin-majesty .ww-gap {
  text-align: center;
  color: #5a4328;
  letter-spacing: 0.3em;
  padding: 4px 0;
}

body.skin-majesty .ww-none {
  color: #5a4328;
  text-align: center;
  padding: 24px 12px;
}


/* ══════════════════════════════════════════════════════════════════════
   THE COZY BUTTON, WHEREVER A BUTTON IS PRESSED (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-29: "the chat bar plus the buttons when you target
   something need to be cosy buttons."

   These two were the last places still drawing a CSS border: 3px of gold
   around a flat navy fill, which is a web button wearing the palette. The
   Cozy plates are already the button everywhere else in this skin (the
   rail's doors, the map's tools), so this is one rule keyed on the shape
   rather than four copies of the same border-image block.

   16x16 art, 5px corner, at x2 — the same slice the map plates use, so a
   chat button and a tool button are visibly the same object at the same
   scale rather than two things that happen to be brown.

   THE WORLD'S FLOOR BUTTONS JOIN THE LISTS (#2236). The chest OPEN and the
   gather button (MINE / CHOP / CAST) wore a leather plank; Wray's verdict
   2026-09-05: "the leather buttons need replacing again with current button
   style" — which is this rule, so they are two more names on it rather than
   a copy of it. Their modules keep only the seat, the press, and a plain
   fallback plate for a page this skin never reaches.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty #world-chest-open,
body.skin-majesty #world-bag-take,
body.skin-majesty #world-gather-mine,
body.skin-majesty .wt-act,
body.skin-majesty #world-chat .wp-verb,
body.skin-majesty #world-chat button[type='submit'] {
  border-style: solid;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 10px;
  border-width: 10px;
  border-image-repeat: repeat;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  color: var(--sk-ink);
  text-shadow: none;
  cursor: pointer;
}

body.skin-majesty #world-chest-open:hover,
body.skin-majesty #world-bag-take:hover,
body.skin-majesty #world-gather-mine:hover,
body.skin-majesty .wt-act:hover,
body.skin-majesty #world-chat .wp-verb:hover,
body.skin-majesty #world-chat button[type='submit']:hover {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
  background: none;
  color: var(--sk-ink);
}

body.skin-majesty #world-chest-open:active,
body.skin-majesty #world-bag-take:active,
body.skin-majesty #world-gather-mine:active,
body.skin-majesty .wt-act:active,
body.skin-majesty #world-chat .wp-verb:active,
body.skin-majesty #world-chat button[type='submit']:active {
  border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a');
}

/* THE ONE THAT SENDS, and the speech verb you are on, take the bright
   plate — the same distinction gold makes on the ribbons, in the material
   the buttons are made of rather than a colour laid over them. */
body.skin-majesty #world-chat .wp-verb[aria-pressed='true'],
body.skin-majesty #world-chat button[type='submit'] {
  border-image-source: url('/assets/ui/btn-bright.png?v=2026-08-29a');
  color: var(--sk-ink-on-gold);
}
body.skin-majesty #world-chat .wp-verb[aria-pressed='true']:hover,
body.skin-majesty #world-chat button[type='submit']:hover {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
}

/* the input keeps a drawn WELL rather than a plate: it is a hole you type
   into, not a thing you press */
body.skin-majesty #world-chat input {
  border-style: solid;
  border-image-source: url('/assets/ui/panel-well.png?v=2026-08-29a');
  border-image-slice: 4 fill;
  border-image-width: 8px;
  border-width: 8px;
  border-image-repeat: repeat;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
}

/* ⛔ THE GATHER BAR IS NO LONGER A PLATE, so the well that dressed it is
   gone (#2252, locked 2026-09-19). The bar became STRIKES: an icon window,
   a ribbon carrying eight orbs, a tier tag and a payout that flies into the
   Bag. Its art IS its design rather than a dressing over a box, so it
   carries its own chrome in `public/js/world-gathering.js` — every piece a
   shipped file in /assets/ui, every offset read off that art. There is
   nothing left here for the skin to dress; the gather BUTTON is still on
   the plate lists above, with the chest's and the chat's. */


/* ══════════════════════════════════════════════════════════════════════
   THE TARGET PANE: MODEL BEHIND, WORDS IN FRONT (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-29: "id like the the object/player selected to be the base
   layer, the Name, with a Large first letter and smaller follow up like it
   is now but to the left of the frame, if its an item, the items
   description, if its a player it will be their level and perhaps house
   too. probably the buttons for player interactions like add friend trade,
   profile etc can live as cosy icons."

   The canvas fills the pane and the words sit over its left half. Nothing
   is boxed: the model stands on the rail and the text is on top of it,
   which is why the left half of the stage is where the model must NOT be
   -- frameStage() offsets it right, so the words never land on the thing
   they are naming.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty #wt-target {
  position: fixed;
  right: 0;
  bottom: var(--sk-foot);
  width: var(--fx, 320px);
  /* ONE GROUND FOR STAGE AND WORDS: the glow covers the whole pane and
     fades on every side, so nothing ends anywhere. Centred where the
     subject now stands (centre, above the title). */
  background: radial-gradient(ellipse 70% 55% at 50% 38%,
    rgba(202, 169, 130, .18), rgba(10, 14, 19, 0) 70%);
  /* 260, was 210 (Wray, 2026-09-04: "no clipping on the stage frame/light").
     The pool stopped clipping when the mask went; what still clipped was the
     SUBJECT -- a standing cat's ears crossed the pane's top edge and were
     cut. The words gained size in the same pass, so the pane pays for both. */
  height: 260px;
  padding: 0;
  box-sizing: border-box;
  display: block;               /* not a column any more: these are layers */
  z-index: 41;
}

/* NO INSET, AND A FADED EDGE. Wray: "i dont like how the light stage effect
   is cut off by black line". The canvas was inset from the pane, so its top
   edge was a straight boundary across the render -- exactly the black line.
   Filling the pane removes the boundary, and the mask feathers whatever is
   left so the stage ends by fading rather than by stopping. */
body.skin-majesty #wt-target .wt-stagec {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* THE RENDER STOPS ABOVE THE WORDS. The pane owns the glow now, so the
     canvas edge draws nothing and can land wherever the layout wants --
     which is the whole trick: the subject gets the top of the pane, the
     title zone gets the bottom 84px, and no amount of camera framing can
     sit a chair on the words again. Explicit sizes, never auto: a replaced
     element with auto size ignores its insets and sits at intrinsic size,
     which was the original crop line. */
  width: 100%;
  height: calc(100% - 84px);
  image-rendering: pixelated;
  /* NO MASK. It was there to feather the canvas edge, and instead it cut the
     light pool into a straight line partway down — a hard edge introduced by
     the very thing meant to remove one. The pool has its own dithered falloff
     now, and the canvas is transparent everywhere else, so there is nothing
     left for a mask to soften. */
}

/* THE NAME SITS AT THE FOOT, ON THE LEFT. Wray's sketch, 2026-08-29: the cat
   stands on its ellipse with the name written under it at the bottom left and
   the level and house on the line below, and the actions stacked as icons
   down the right-hand side.

   That is a better arrangement than the one it replaces, and for a reason
   worth keeping: at the top the words sat where the subject's head is, so a
   tall thing pushed into them. At the foot they are under the pool, which is
   the one part of the stage nothing ever occupies. */
body.skin-majesty .wt-info {
  position: absolute;
  left: 16px;
  bottom: 10px;
  width: 62%;
  z-index: 2;
  text-align: left;
  /* the words are over a 3D render, so they carry their own contrast rather
     than relying on whatever happens to be behind them */
  text-shadow: 0 2px 0 #0a0e13, 0 0 6px rgba(10, 14, 19, .95);
}

body.skin-majesty .wt-info .wt-name {
  display: flex;
  align-items: baseline;
  gap: 0;
  font-family: var(--sk-tome);
  /* 48 (Wray, same day, seeing 32: "it could be slightly bigger still").
     The face's grid allows 32 or 48 and nothing between -- 40 would be
     uneven stems -- so "slightly" buys the whole step. If this crowds a long
     handle the wrap rule below is what gives. */
  font-size: 48px;
  line-height: 1.05;
  color: #f0e4cd;
  /* one line, always: paint() steps the size down until it fits, so wrap
     and mid-word breaks are the failure modes, not the fallbacks */
  white-space: nowrap;
  overflow: hidden;
}

/* the calligraphic initial, kept legible. Wray on the first attempt: "its
   too large to be legible" -- so the letterform is the flourish and the size
   barely moves. */
body.skin-majesty .wt-info .wt-cap {
  color: #a4343a;
  font-size: 1.3em;
  /* "the first letter bumped higher too" (Wray, 2026-09-04): the oversize
     cap hung below the baseline; riding it up aligns its crown with the
     word's rather than its feet with the descenders. */
  position: relative;
  top: -0.12em;
  line-height: 1;
}

body.skin-majesty .wt-info .wt-sub {
  margin-top: 4px;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  line-height: 1.25;
  color: #c6b393;
}

/* THE ACTIONS: cosy plates, icon only, along the foot of the pane. */
/* A COLUMN DOWN THE RIGHT, which is where the sketch puts them: four circles
   stacked beside the cat rather than a row under it. It also keeps them clear
   of the name, which a bottom row could not do once the name moved down. */
body.skin-majesty #wt-target .wt-acts {
  position: absolute;
  right: 14px;
  top: 10px;
  bottom: 10px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: flex-end;
  justify-content: center;
}

body.skin-majesty #wt-target .wt-act {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  box-sizing: border-box;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0;                 /* the label is the tooltip, not the face */
}

body.skin-majesty #wt-target .wt-act .ws-ico {
  width: 16px;
  height: 16px;
  margin: 0;
}


/* ══════════════════════════════════════════════════════════════════════
   THE TOP THREE ARE PLATED (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-30: "i want a special effect like the item-blueprint.html
   and that workflows effects so the top 3 have a fancy fill on their
   entry."

   That sheet's vocabulary is METAL, built from three things rather than a
   single gradient: a brushed grain running at an angle, an inner scribe
   line where the plate is pressed, and a bevel. A flat gold bar reads as a
   coloured rectangle; those three together read as a thing made of metal,
   which is the whole difference.

   Gold, silver, bronze, in that order, because that is the one ranking
   convention nobody has to be taught. Each is the same construction with
   its own three tones, so a fourth could be added without a new rule.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ww-rank.is-top {
  position: relative;
  isolation: isolate;           /* the grain must not escape onto the page */
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, .45),      /* the scribe line */
    inset 0 1px 0 rgba(255, 255, 255, .28),  /* the bevel, lit from above */
    inset 0 -1px 0 rgba(0, 0, 0, .35);
  background-image:
    /* the brushed grain, at the same 160deg the blueprint uses */
    repeating-linear-gradient(160deg,
      rgba(255, 255, 255, .10) 0 1px,
      rgba(0, 0, 0, 0) 1px 3px,
      rgba(0, 0, 0, .10) 3px 4px,
      rgba(0, 0, 0, 0) 4px 8px),
    linear-gradient(180deg, var(--pl-hi), var(--pl-mid) 55%, var(--pl-lo));
  color: #2a1c0c;
}

/* the banding underneath would show through the plate */
body.skin-majesty .ww-rank.is-top:nth-child(even) { background-color: transparent; }

body.skin-majesty .ww-rank.is-top .ww-rk,
body.skin-majesty .ww-rank.is-top .ww-note { color: #4a3312; }
body.skin-majesty .ww-rank.is-top .ww-score { color: #2a1c0c; }
body.skin-majesty .ww-rank.is-top .ww-cap { color: #6b2226; }

body.skin-majesty .ww-rank.is-gold   { --pl-hi: #f4d98a; --pl-mid: #d8a657; --pl-lo: #a97c32; }
body.skin-majesty .ww-rank.is-silver { --pl-hi: #e6e9ef; --pl-mid: #b9c0cc; --pl-lo: #8b93a1; }
body.skin-majesty .ww-rank.is-bronze { --pl-hi: #e0a878; --pl-mid: #b97843; --pl-lo: #8a5528; }

/* THE RIVETS, one at each end, the blueprint's own detail. Two pseudo
   elements rather than four, because at 34px tall a row only has room for a
   pair and four would read as noise. */
body.skin-majesty .ww-rank.is-top::before,
body.skin-majesty .ww-rank.is-top::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2px;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%,
    rgba(255, 255, 255, .9), var(--pl-mid) 58%, var(--pl-lo));
  box-shadow: 0 1px 0 rgba(0, 0, 0, .5), inset 0 -1px 0 rgba(0, 0, 0, .4);
  z-index: 1;
}
body.skin-majesty .ww-rank.is-top::before { left: 4px; }
body.skin-majesty .ww-rank.is-top::after { right: 4px; }

@media (prefers-reduced-motion: reduce) {
  body.skin-majesty .ws-tabs .wr-plank { transition: none; }
}


/* ── the stall (#2020) ──────────────────────────────────────────────── */

body.skin-majesty .ww-packhead { text-align: center; margin-bottom: 4px; }

body.skin-majesty .ww-claim {
  display: block;
  margin: 8px auto;
  padding: 0 18px;
  height: 40px;
  box-sizing: border-box;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 9px;
  letter-spacing: .06em;
  color: var(--sk-ink-on-gold);
  border-style: solid;
  border-image-source: url('/assets/ui/btn-bright.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 10px;
  border-width: 10px;
  border-image-repeat: repeat;
  background: none;
  image-rendering: pixelated;
  cursor: pointer;
}
body.skin-majesty .ww-claim:hover {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
}
body.skin-majesty .ww-claim:disabled { opacity: .6; cursor: default; }

body.skin-majesty .ww-packs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  align-content: flex-start;
  padding: 6px 0;
  overflow-y: auto;
}

/* A SEALED PACK IS A THING YOU PRESS, so it is a plate rather than a card:
   the card is what is inside it, and drawing one on the outside would be
   promising a specific card before it has been rolled. */
body.skin-majesty .ww-pack {
  flex: 0 0 auto;
  width: 116px;
  height: 92px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-style: solid;
  border-image-source: url('/assets/ui/panel-plate.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 12px;
  border-width: 12px;
  border-image-repeat: repeat;
  background: none;
  image-rendering: pixelated;
  cursor: pointer;
  color: var(--sk-ink);
}
body.skin-majesty .ww-pack:hover {
  border-image-source: url('/assets/ui/panel-lit.png?v=2026-08-29a');
}
body.skin-majesty .ww-pack:disabled { cursor: default; }

body.skin-majesty .ww-pack-kind {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  text-align: center;
  line-height: 1.1;
}
body.skin-majesty .ww-pack-open {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .08em;
  color: var(--sk-gold-hi);
}


/* ══════════════════════════════════════════════════════════════════════
   FULL SCREEN (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-30: "a fullscreen toggle button that takes over both rails
   so you see only the arena window fullscreen."

   The rails, the tray, the foot and the target pane are not the window's
   children, so this is a class on <body> and everything moves out of the
   way from one place. The window itself stops being centred on the render
   rectangle and takes the viewport, which is the one time the whole-number
   letterbox is not what you want -- you asked for the window, not the
   picture behind it.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty.ws-full .ws-rail,
body.skin-majesty.ws-full #world-chat,
body.skin-majesty.ws-full #wt-foot,
body.skin-majesty.ws-full #wt-target,
body.skin-majesty.ws-full #wt-rail,
body.skin-majesty.ws-full #wt-frame { display: none; }

body.skin-majesty.ws-full .ws-sheet {
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  /* a dragged offset would put a full-screen window off the edge */
  translate: none;
}

body.skin-majesty.ws-full .ws-tabs.ws-tabs-r {
  left: auto;
  right: 0;
  top: 78px;
  translate: none;
}

/* ══════════════════════════════════════════════════════════════════════
   THE ZONE MAP (#2020)
   ──────────────────────────────────────────────────────────────────────
   A pin per zone on Majesty's parchment. The creases are drawn rather than
   painted into the art, so the map is the same sheet as every other window
   and gains its folds from two repeating gradients — which also means the
   grid does not stretch when the window is resized, because it is drawn at
   a fixed pitch rather than scaled with an image.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ww-map {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  min-height: 0;
  display: flex;
  align-items: stretch;
  justify-content: center;
}

/* THE PACK'S OWN MAP, now that its licence is confirmed (Wray, 2026-08-30).
   The drawn gradients it replaces were a stand-in for exactly this: a page
   with fold creases, a compass rose and a curled corner, which is a better
   map than four repeating-linear-gradients pretending to be one.

   162x130 art at a WHOLE x3 -- 486x390. Not stretched to fit: this is a
   picture, not a nine-slice, so any fractional scale would soften the compass
   and the creases, and those are the whole reason to use it. The Arena window
   is sized around it below rather than the other way round. */
body.skin-majesty .ww-mapsheet {
  position: relative;
  flex: 0 0 auto;
  align-self: center;
  width: 486px;
  height: 390px;
  margin: 0;
  background: url('/assets/ui/map-sheet.png?v=2026-08-30a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
}
/* ⭐x2 IN A NARROW PANEL (2026-09-12). Play is a docked panel now, .24 of the
   picture -- 461px at 1920, ~353 inside the chrome -- and a 486px map inside
   that was cropped on the right. Whole multiples only, so the sheet steps
   DOWN to 324x260 (162x130 art at x2) rather than scaling to fit; the pins
   are placed in percentages so they follow. The sheet body is the container
   (declared beside its rule), so this keys off the room the window actually
   gives, not off the screen. */
@container ws-body (max-width: 520px) {
  /* and it sits at the TOP of the panel rather than floating mid-way down a
     tall column -- a docked panel reads top-down like a list, and Albion
     never leaves the head of a panel empty */
  body.skin-majesty .ww-mapsheet { width: 324px; height: 260px; align-self: flex-start; }
}

/* the Arena window is sized around its map, which is the one window whose
   content has a fixed size rather than a flexible one */
/* THE ARENA WINDOW IS NAVY, not parchment. Wray, 2026-08-30: "let put it on
   a navy frame." The map is itself a sheet of paper, so a parchment window
   round it was paper on paper -- two creamy rectangles with nothing between
   them. The navy screen is the pack's other ground and it makes the map read
   as a thing lying ON something. */
/* A BAG THAT LEAVES THE ROOM VISIBLE. Wray, 2026-08-30: "its too big to be
   a ui that would would open and drop furniture on the ground from ... bag
   needs to feel like a bag, not taking up all the screen."

   Six columns of 40 plus gaps is 255, plus the 32px borders and the 22px
   gutters that clear the ribbon strips. Small enough to place a chair with
   it open, which is the test that failed before. */
/* FRAME B, navy. Wray, 2026-08-30: "Frame B definately for the bag."
   The bag's cells and its frame become one family, so it reads as a
   container rather than a page, and it stops competing with the parchment
   windows for attention. */
/* THE ASK, PER DOOR (#2557). Set on the sheet AND its strips: the strips are
   siblings and take their place from the same two numbers. A bare `width:`
   here is the trap this replaces -- the sheet obeyed it and the strips did
   not, so the Bag's ribbons were laid out for a 680px window it never drew. */
body.skin-majesty .ws-sheet[data-door='nine'],
body.skin-majesty .ws-tabs[data-door='nine'] {
  /* GRID. 8 cells x 40 + gaps + the parchment's own 32px borders + the
     window's 36px frame, and tall enough for six rows rather than four --
     "bag window too small... need to see more slots" (Wray, 2026-09-05).
     A SHARE OF THE PICTURE since 2026-09-08 (see the ask block at the top).
     27% lands on 518px at 1920, which is where the bag already was, so the
     eight columns are undisturbed -- but it now holds that proportion on
     every monitor instead of only on his. The 500px floor is the content's
     own arithmetic: below it the eighth column is cut, which is a bug this
     arc has already fixed once. */
  /* ⭐MEASURED OFF THE REAL ALBION INVENTORY (#2020, 2026-09-08): 23.6% wide
     by 81.1% tall, right-docked. Ours was 27.1% x 66.0% -- wider and 15
     points shorter, which is most of why it read as a squat box where theirs
     reads as a bag.

     ⛔THE WIDTH IS FLOOR-BOUND AT 1920 AND THIS SHARE CHANGES NOTHING THERE.
     .24 of 1920 is 461px, under the 520px floor below, so the floor wins and
     the window still draws 520. The floor is load-bearing: eight columns of
     48px at a 3px gap is 405px of cells plus 108px of chrome = 513. The share
     only bites above ~2167px wide. Narrowing at 1920 means dropping a column
     or shrinking the cell, and eight columns is Wray's own 2026-09-05 pick
     ("need to see more slots in the bag"). Said out loud because a share that
     silently does nothing is the kind of thing that gets "fixed" twice. */
  --sk-win-wpct: .24;
  /* .88 rather than .81: the band clamp above is what actually stops it, and
     with the tray freed on this side that lands the bottom edge 8px above the
     foot bar -- Wray's "extend lower probably to the top of where the zoom
     buttons are". Measured: 950px at 1080, ending at 1034, foot at 1042. */
  --sk-win-hpct: .88;
  /* ⛔520 IS ARITHMETIC, NOT A ROUND NUMBER. The grid draws 8 cells at
     --sk-cell 48 with a 3px gap = 405px, and the sheet's frame plus the
     body's padding take 108, so anything under 513 clips the eighth column --
     which is the exact bug `__tests__/bagGridFits.test.js` exists about, and
     which a 500px floor reintroduced the moment the cell went 40 to 48.
     The test derives this rather than repeating it, so a future change to the
     cell, the column count or the gap has to satisfy it again. */
  --sk-win-wmin: 520px;
}
/* ⭐THE BAG IS PARCHMENT LIKE EVERY OTHER DOOR (Wray, 2026-09-07, answering
   the kit's section 10 pick: "A parchment and we can use the ribbons for
   tabs"). Option A: one material, every window the same page.

   What is deleted here is the navy `screen.png` nine-slice this door wore,
   the two inks that went with it (a cream title and a blue-grey foot, both
   picked to sit ON navy), and the paper the door had to lay UNDER a framed
   tab because its own ground was dark (#2558). With the sheet itself paper,
   all four are the default: the shared `.ws-sheet` rule further up draws
   `paper.png`, and the generic `.ws-sheet-body iframe { background:
   transparent }` lets that paper show through a framed page.

   ⛔THE COST, NAMED: the Bag went navy because a grid of item slots wants
   contrast behind the art. The slots are the Majesty pack's own
   `slot-holder.png` (#2591), which is a parchment square, so they now sit
   paper-on-paper and the rarity rim is doing more of the work. If that reads
   flat, the answer is the CELL, not the window. */
body.skin-majesty .ws-sheet[data-door='arena'],
body.skin-majesty .ws-tabs[data-door='arena'] {
  /* VIEW. A map is looked through, not read: the widest share and the
     shortest, because a zone map gains nothing from height the way a list
     does.

     ⛔THE FLOOR WAS SIZED AGAINST THE WRONG DENOMINATOR, and it had been
     quietly beating the share ever since (found 2026-09-09). The note here
     used to say "30% x 50% lands on 576 x 496 at 1920" -- but .30 of 1920 is
     the SCREEN, and every share in this file is of the PICTURE, which is
     1280 wide on a 1920 screen. The real ask was 384px, the 440px floor won
     every time, and the door rendered at 34.4% of the picture: outside the
     docked band the corner pass sets, while the corner pass passed, because
     it reads the declared share and never sees the floor override it.

     .32 is the band's ceiling and 410px at 1280, which is what these rows
     actually need -- measured: at 440 the description wrapped hard against
     the action label so "Deploy a Nine with three cards and" and "Pick a
     zone" read as one run-on line. The floor drops to 360 so it is a floor
     for a small picture rather than the number in charge. */
  /* ⭐A LEFT PANEL, THE SAME PANEL AS EVERY OTHER LEFT PANEL (Wray,
     2026-09-12: "our left right and central displays are appropriate size to
     then get our info into"). The .32 x .52 above was the VIEW shape from
     when this door was the zone map; it is a list of modes now, and the map
     is a tab inside it. Three placements, three shapes: a docked panel is
     .24 x .88 whichever side it hangs on, a centred window is .43 x .71.
     The map steps down to its x2 art inside the narrower panel (see the
     container rule on `.ww-mapsheet`), so nothing is cropped. */
  --sk-win-wpct: .24;
  --sk-win-hpct: .88;
  --sk-win-wmin: 360px;
  --sk-win-hmin: 300px;
}
/* ⭐THE ARENA IS PARCHMENT TOO, AND STILL THIN. Two rulings meet here and
   both survive.

   Wray, 2026-08-30: "starting to doubt the arena frame now but i like the
   map" -- 36px of heavy navy around a live scene is a lot of furniture
   around something meant to be looked THROUGH. That was about the WEIGHT of
   the edge, not the door, so it outlives the material.

   Wray, 2026-09-07: "A parchment." So the source becomes the shared
   `paper.png` and only the width stays overridden.

   ⭐16px, NOT the 18 the navy wore. paper.png's torn edge is 16 art-pixels;
   the shared rule draws it at 32 (a clean 2x) and 18 would be 1.125x, which
   on a pixel-art edge is the uneven-scale trap #2206 exists about. 16 is 1x:
   thinner than every other door, crisp, and a whole multiple. The title
   colour goes with the navy it was picked for -- ink on paper is the
   default. */
body.skin-majesty .ws-sheet[data-door='arena'] {
  /* ⛔AND ITS OWN SLICE, since the shared sheet's went 16 -> 28 (the torn bite
     had to leave the repeating edge strip, or `round` tiled it into a row of
     tabs down the side). This door deliberately keeps a THINNER edge -- that
     ruling was about weight, not material -- and 16 is not a whole multiple of
     28, so inheriting would draw its chrome at 0.57 of an art pixel and blur
     it, which is the #2206 uneven-scale trap. 16/16 keeps both rulings. */
  border-image-slice: 16;
  border-image-width: 16px;
  border-width: 16px;
}

/* THE OTHER DOORS, BY WHAT THEY HOLD (#2557). Four shapes, chosen by content:
   readout (a few numbers, a short form), list (rows you scan), grid (tiles
   you pick from), view (a picture you look through). Until now every door not
   named above opened at the 680x470 default whatever it held: sixty
   leaderboard rows sliced at the twelfth, a one-line guild notice in a slab
   the size of the bag. These are starting shapes at chrome pixel 2, sized to
   what each door draws today; Wray tunes them by eye on the livereload stack,
   not by re-deriving them here. Character is not in this list: it is its own
   window (`.wc-window`), clamped further down.

   ⭐EACH SHAPE IS A SHARE OF THE PICTURE (2026-09-08). Read the ask block at
   the top of this file first: it carries the Albion measurement these are
   pulled toward, and the note that only READOUT is that measurement rather
   than a derivation from it.

   Where each number comes from, so none of them reads as taste:

     readout  .215 x .62  Albion's Stats window exactly (324 x 576 of
                          1509 x 923). A settings form is a label and a
                          value, which is what that window holds.
     list     .24  x .66  a ladder row carries rank, name, score and a
                          sigil, so it needs more across than a label and a
                          value -- but `.ww-table` caps itself at 520px, so
                          past that the window is growing parchment around a
                          table that is not using it.
     grid     .27  x .66  the widest thing we draw is the bag's eight
                          columns. Their Appearance window is wider than
                          their Stats for the same reason.
     view     .30  x .50  a map is looked through: the widest share, the
                          shortest, and its own thin frame above.

   ⛔WHAT THIS COSTS, NAMED. Every list door gets SHORTER: the board goes
   from an 880px ask (which the clamp let fill the whole band) to 66% of the
   picture. That reverses his 2026-09-06 "fix the length... so you can fit
   the info to those boards" in favour of his 2026-09-08 ruling, which is
   the later one. The rows survive the cut because `.ww-table` scrolls; what
   he was actually seeing in 09-06 was a row sliced at the twelfth, and that
   was fixed by the list class rather than by the height.

   These are still ASKS. The clamp on `.ws-sheet` takes the smaller of the
   ask and the band, so a phone is unaffected and no window can leave the
   picture. Character is not in this list: it is its own window
   (`.wc-window`), clamped further down. */
body.skin-majesty .ws-sheet[data-door='rooms'],
body.skin-majesty .ws-tabs[data-door='rooms'] { --sk-win-wpct: .24; --sk-win-hpct: .88; }      /* list */
body.skin-majesty .ws-sheet[data-door='board'],
body.skin-majesty .ws-tabs[data-door='board'] { --sk-win-wpct: .24; --sk-win-hpct: .88; }      /* list */
body.skin-majesty .ws-sheet[data-door='guild'],
body.skin-majesty .ws-tabs[data-door='guild'] { --sk-win-wpct: .24; --sk-win-hpct: .88; }      /* list */
body.skin-majesty .ws-sheet[data-door='stall'],
body.skin-majesty .ws-tabs[data-door='stall'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }   /* centred view */
/* ⭐THE STORE'S SHAPE, because it is the Store's KIND of window (#2020,
   Wray's pick 2026-09-09). At .24 with a 520px floor the floor always won --
   measured 40.6% x 85.2% of the picture at 1920x1080, which is the centred
   window's width at the docked panel's height. Albion's centred marketplace is
   42.5% x 71.1%; these are the numbers the Store already carries. */
body.skin-majesty .ws-sheet[data-door='spellbook'],
body.skin-majesty .ws-tabs[data-door='spellbook'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }  /* centred view */
/* THE FORGE, the centred shape (#3057, Wray's FORGE 1B: "it needs its
   space", for spellcraft later). Its drawing is world-forge.css. */
body.skin-majesty .ws-sheet[data-door='forge'],
body.skin-majesty .ws-tabs[data-door='forge'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }  /* centred view */
/* THE SMITHY, the Forge's shape (#3030, Wray's SMITHY 1A: "the same shape as
   the Forge, so both anvils work the same way"). Its drawing is
   world-bench.css and world-smithy.css. */
body.skin-majesty .ws-sheet[data-door='smithy'],
body.skin-majesty .ws-tabs[data-door='smithy'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }  /* centred view */
/* THE REFINING WINDOW, the same shape (#3345, SMITHY 6A: one bench grammar).
   Its drawing is world-bench.css and world-refine-bench.css. */
body.skin-majesty .ws-sheet[data-door='refine'],
body.skin-majesty .ws-tabs[data-door='refine'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }  /* centred view */
/* THE COOKFIRE, the same shape (#3619, SMITHY 6A: one bench grammar).
   Its drawing is world-bench.css and world-cookfire.css. */
body.skin-majesty .ws-sheet[data-door='cook'],
body.skin-majesty .ws-tabs[data-door='cook'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }  /* centred view */
/* THE ALCHEMY STAND (#3620 slice 4c): the cookfire's share, its twin. */
body.skin-majesty .ws-sheet[data-door='alchemy'],
body.skin-majesty .ws-tabs[data-door='alchemy'] { --sk-win-wpct: .43; --sk-win-hpct: .71; --sk-win-wmin: 560px; }  /* centred view */
/* ⭐SETTINGS IS A RIGHT PANEL, and takes the right panel's shape (Wray,
   2026-09-12, the three-shapes ruling above). It was .215 x .62, Albion's
   Stats window copied exactly -- but their Stats is a CENTRED readout and
   ours hangs on the right beside the Bag, which is .88 tall. Two heights on
   one edge read as two different systems; and Help and Profile are long
   framed pages that were scrolling inside 62% of the picture for no
   reason. */
body.skin-majesty .ws-sheet[data-door='record'],
body.skin-majesty .ws-tabs[data-door='record'] { --sk-win-wpct: .24; --sk-win-hpct: .88; --sk-win-wmin: 360px; }     /* dock */
/* The quest log is rows you scan with a description under each, so it takes
   the LIST shape rather than the shared default it would otherwise inherit.
   Added with the door itself (#2557): a door that declares no share is not a
   deliberate choice, it is one that nobody made, and the suite says so. */
body.skin-majesty .ws-sheet[data-door='quests'],
body.skin-majesty .ws-tabs[data-door='quests'] { --sk-win-wpct: .24; --sk-win-hpct: .88; }     /* list */

/* A PIN IS A PLACE, NOT A BUTTON, so it is a mark with a label beside it
   rather than a plate. The plate treatment is for things you press in a
   list; a map wants the parchment to read as the surface. */
/* ANCHORED BY THE DOT, NOT THE BOX (Wray, 2026-09-04: "the Zones are
   bleeding off the page"). translate(-50%) centred the whole dot+label box
   on the zone's point, so the label's length pushed everything sideways and
   eastern zones ran off the parchment. Now the DOT sits on the point and the
   label hangs off it -- eastward by default, WESTWARD (.is-west, stamped by
   world-windows.js for pins past halfway) so labels grow into the page
   instead of off its edge. */
body.skin-majesty .ww-poi {
  position: absolute;
  transform: translate(-8px, -50%);   /* dot half-width: the dot is the anchor */
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 4px;
  border: 0;
  background: none;
  cursor: pointer;
  color: #3a2a18;
  text-align: left;
  white-space: nowrap;
}
body.skin-majesty .ww-poi.is-west {
  flex-direction: row-reverse;
  transform: translate(calc(-100% + 8px), -50%);
  text-align: right;
}

body.skin-majesty .ww-poi-dot {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  background: #a4343a;
  box-shadow: 0 0 0 2px rgba(58, 42, 24, .55), 0 2px 0 rgba(0, 0, 0, .3);
  transform: rotate(45deg);
}

body.skin-majesty .ww-poi.is-objective .ww-poi-dot { background: #d8a657; }
body.skin-majesty .ww-poi.is-event .ww-poi-dot { background: #5b8fe0; }

body.skin-majesty .ww-poi-tag { display: flex; flex-direction: column; }
/* the backstop: a very wordy zone ellipsises instead of leaving the sheet */
body.skin-majesty .ww-poi-tag b,
body.skin-majesty .ww-poi-tag i {
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.skin-majesty .ww-poi-tag b {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 17px;
  line-height: 1;
  font-weight: 400;
}
body.skin-majesty .ww-poi-tag i {
  font-style: normal;
  font-size: 12px;
  color: #5a4328;
  line-height: 1.2;
}

body.skin-majesty .ww-poi:hover .ww-poi-tag b,
body.skin-majesty .ww-poi:focus-visible .ww-poi-tag b { color: #a4343a; }
body.skin-majesty .ww-poi:hover .ww-poi-dot { transform: rotate(45deg) scale(1.3); }


/* ── THE CHARACTER SHEET (#2354): doll, numbers, tray ─────────────────
   WoW's arrangement in the drawn chrome: slots flank the portrait, the
   numbers sit under the doll, and the tray at the foot fills when a slot
   is tapped. The preview column is the reason this window exists -- "see
   your stats change when you change items" -- so it gets the accent and
   everything else stays quiet. */
body.skin-majesty .wc-root { display: flex; flex-direction: column; gap: 10px; }
body.skin-majesty .wc-doll {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 14px;
  padding-top: 6px;
}
body.skin-majesty .wc-col { display: flex; flex-direction: column; gap: 8px; }
body.skin-majesty .wc-portrait {
  position: relative;
  width: 128px;
  height: 128px;
  border: 3px solid var(--sk-gold);
  background: var(--sk-hole);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
body.skin-majesty .wc-portrait canvas { width: 100%; height: 100%; image-rendering: pixelated; }
body.skin-majesty .wc-noface { font-family: var(--sk-tome); font-size: 32px; color: var(--sk-ink-dim); }
/* guild coin badge (#2404) — the guild's coin logo on the portrait corner */
body.skin-majesty .wc-guildbadge {
  position: absolute;
  top: 3px; left: 3px;
  width: 26px; height: 26px;
  object-fit: cover;
  border: 2px solid var(--sk-gold);
  background: var(--sk-hole);
  image-rendering: auto;
  z-index: 2;
}
body.skin-majesty .wc-nname {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  font-style: normal;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  text-align: center;
  padding: 3px 2px;
  color: var(--sk-ink);
  background: rgba(10, 14, 19, .72);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.skin-majesty .wc-slot.is-picked { outline: 2px solid var(--sk-gold-hi); outline-offset: 1px; }
body.skin-majesty .wc-mark {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px; color: var(--sk-ink-dim);
}
body.skin-majesty .wc-stats {
  display: flex;
  justify-content: center;
  gap: 18px;
  padding: 4px 0;
  border-top: 1px solid var(--sk-navy-lift);
  border-bottom: 1px solid var(--sk-navy-lift);
}
body.skin-majesty .wc-stat {
  display: flex; align-items: baseline; gap: 6px;
  font-family: var(--sk-body, 'VT323', monospace); font-size: 19px;
}
body.skin-majesty .wc-stat b {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px; color: var(--sk-ink-dim); font-weight: 400;
}
body.skin-majesty .wc-num { color: var(--sk-ink); }
/* the preview: where the number WOULD land. Arrowed by the > glyph as well
   as colour, because colour alone is the anti-pattern. */
body.skin-majesty .wc-preview { min-width: 40px; }
body.skin-majesty .wc-preview.is-up { color: #68c46f; }
body.skin-majesty .wc-preview.is-down { color: var(--sk-hot); }
body.skin-majesty .wc-build {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 2px 0;
}
body.skin-majesty .wc-buildlab {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px; color: var(--sk-ink-dim); font-weight: 400;
  margin-right: 6px;
}
body.skin-majesty .wc-bslot.is-picked { outline: 2px solid var(--sk-gold-hi); outline-offset: 1px; }
body.skin-majesty .wc-tray { min-height: 90px; }
body.skin-majesty .wc-trayhead {
  display: flex; align-items: center; gap: 12px; padding: 2px 2px 8px;
  font-family: var(--sk-body, 'VT323', monospace); font-size: 19px; color: var(--sk-ink);
}
body.skin-majesty .wc-takeoff {
  border: 0; background: none; cursor: pointer;
  font-family: var(--sk-body, 'VT323', monospace); font-size: 17px;
  color: var(--sk-ink-dim); text-decoration: underline;
}
body.skin-majesty .wc-takeoff:hover { color: var(--sk-hot); }

/* ── the bag's sort tabs (#2355): ink chips over the grid ─────────────
   Text, not planks: seven slots plus All is eight controls, and eight
   nine-sliced buttons above a grid of nine-sliced cells is chrome eating
   the page. Ink on the parchment, gold when chosen, same statement the
   ribbons make at a fraction of the weight. */
body.skin-majesty .ww-bagtabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 10px;
  padding: 2px 2px 8px;
}
/* RIBBONS, LIKE EVERY OTHER PAGE (Wray, 2026-09-04: "bag tabs look good
   but they should be ribbons like how all other pages work"). Same tab art
   the window strips wear, at x2 -- small enough that eight of them still
   read as a filter row rather than a second door. */
body.skin-majesty .ww-bagtab {
  border-style: solid;
  border-image-source: url('/assets/ui/tab-off.png?v=2026-09-03a');
  border-image-slice: 0 4 0 1 fill;
  border-image-width: 0 8px 0 2px;
  border-width: 0 8px 0 2px;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  height: 24px;
  box-sizing: border-box;
  padding: 0 6px 2px 8px;
  cursor: pointer;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 17px;
  line-height: 1;
  color: #f6ecd6;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
  text-transform: capitalize;
  display: inline-flex;
  align-items: center;
}
body.skin-majesty .ww-bagtab:hover { color: #ffe9a3; }
body.skin-majesty .ww-bagtab[aria-pressed='true'] {
  border-image-source: url('/assets/ui/tab-on.png?v=2026-09-03a');
  color: #2e2113;
  text-shadow: none;
}

/* ══════════════════════════════════════════════════════════════════════
   $9LV, ON PARCHMENT (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-30: "$9LV window needs converting to parchment styling."

   Everything here is ink on paper: no plates, no wells, no second ground.
   A supply table is a document, and the one visual element -- the split bar
   -- earns it by being the thing you cannot say in a sentence.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ww-token {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  color: #3a2a18;
  padding: 2px 2px 8px;
}

body.skin-majesty .ww-token .ww-h {
  margin: 4px 0 0;
  font-family: var(--sk-tome);
  font-size: 19px;
  color: #3a2412;
}

/* the contract address, which is the one thing anyone came for */
body.skin-majesty .ww-ca {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  box-shadow: inset 0 0 0 2px rgba(122, 96, 58, .45);
}
body.skin-majesty .ww-ca-tag {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  letter-spacing: .1em;
  color: #5a4328;
}
body.skin-majesty .ww-ca-addr {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  color: #3a2a18;
}
body.skin-majesty .ww-ca-copy {
  flex: 0 0 auto;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .08em;
  color: #a4343a;
}

body.skin-majesty .ww-checks { display: flex; flex-wrap: wrap; gap: 6px 18px; }
body.skin-majesty .ww-check { display: flex; gap: 8px; align-items: baseline; }
body.skin-majesty .ww-check .ww-k { color: #5a4328; font-size: 14px; }
body.skin-majesty .ww-check .ww-v {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 17px;
}
/* revoked authorities are the GOOD state, so the colour follows the meaning
   and not the word */
body.skin-majesty .ww-check.is-ok .ww-v { color: #2f6b34; }
body.skin-majesty .ww-check.is-warn .ww-v { color: #a4343a; }

body.skin-majesty .ww-supply {
  display: grid;
  grid-template-columns: auto 1fr auto 1fr;
  gap: 4px 10px;
  align-items: baseline;
}
body.skin-majesty .ww-supply .ww-k { color: #5a4328; font-size: 14px; }
body.skin-majesty .ww-supply .ww-v {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 18px;
  font-variant-numeric: tabular-nums;
}

/* the split. Drawn as one bar rather than three numbers because the shape of
   it is the claim: most of the supply is not going anywhere. */
body.skin-majesty .ww-bar {
  display: flex;
  /* `flex: 0 0 20px`, not `height`. `.ww-token` is a scrolling COLUMN flex, so
     every child is shrinkable on the main axis -- and the bar is the one child
     with no content to hold it open, so it shrank to exactly 0 and the whole
     visual vanished while its three segments still measured 168/68/244 wide.
     Nothing in the declaration looked wrong, which is why it needed measuring
     rather than looking at. */
  flex: 0 0 20px;
  box-shadow: inset 0 0 0 2px rgba(122, 96, 58, .45);
}
body.skin-majesty .ww-seg { display: block; min-width: 2px; }
body.skin-majesty .ww-seg.is-locked { background: #6b2226; }
body.skin-majesty .ww-seg.is-held { background: #a4343a; }
body.skin-majesty .ww-seg.is-circulating { background: #caa982; }

body.skin-majesty .ww-key { display: flex; flex-wrap: wrap; gap: 6px 16px; }
body.skin-majesty .ww-keyi { display: flex; flex-direction: column; }
body.skin-majesty .ww-keyi b {
  font-weight: 400;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  padding-left: 14px;
  position: relative;
}
body.skin-majesty .ww-keyi b::before {
  content: '';
  position: absolute;
  left: 0;
  top: 5px;
  width: 9px;
  height: 9px;
}
body.skin-majesty .ww-keyi.is-locked b::before { background: #6b2226; }
body.skin-majesty .ww-keyi.is-held b::before { background: #a4343a; }
body.skin-majesty .ww-keyi.is-circulating b::before { background: #caa982; }
body.skin-majesty .ww-keyi i {
  font-style: normal;
  font-size: 12px;
  color: #5a4328;
  padding-left: 14px;
}

/* THE LOCKS. Each one is a link to its own contract, with the terms shown
   rather than hidden behind it -- the terms ARE the lock, and a link that
   says "trust me, it is locked" is the thing this page exists to avoid. */
body.skin-majesty .ww-locks { display: flex; flex-direction: column; gap: 8px; }
body.skin-majesty .ww-lock {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 10px;
  padding: 7px 9px;
  text-decoration: none;
  color: #3a2a18;
  box-shadow: inset 0 0 0 2px rgba(122, 96, 58, .35);
  border-left: 5px solid #6b2226;
}
body.skin-majesty .ww-lock:hover { box-shadow: inset 0 0 0 2px #a4343a; }
body.skin-majesty .ww-lock b {
  font-weight: 400;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 18px;
}
body.skin-majesty .ww-lock-amt {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 18px;
  color: #6b2226;
  font-variant-numeric: tabular-nums;
}
body.skin-majesty .ww-lock i {
  grid-column: 1 / -1;
  font-style: normal;
  font-size: 12.5px;
  line-height: 1.4;
  color: #5a4328;
}

body.skin-majesty .ww-more {
  align-self: flex-start;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  color: #a4343a;
}


/* ══════════════════════════════════════════════════════════════════════
   THE HERO FRAME OPENS (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-30: "maybe clicking the hero frame extends its border down
   to reveal item slots you drag item icons into and also build slots
   there."

   So the panel is not a second box under the card: it is the SAME plate,
   continued. The card loses its bottom edge when open and the panel picks
   up the same nine-slice with no top edge, which is what makes the border
   read as having grown rather than as two things stacked.
   ══════════════════════════════════════════════════════════════════════ */

/* NO JOIN TO CLOSE. The panel is a CHILD of the card now, so the card's own
   border wraps both halves and simply gets taller when it opens. Everything
   that used to live here -- the card dropping its bottom cap, the panel
   dropping its top, a negative margin cancelling the rail's row gap -- was
   arithmetic that had to come out exact on every screen, and it did not.
   Deleted rather than tuned again. */
body.skin-majesty .we-slotwrap {
  flex: 1 0 100%;
  min-width: 0;
}

/* ── the open/close, animated ──────────────────────────────────────────
   A grid row from 0fr to 1fr animates the panel's REAL height with no
   number written down, so adding a slot does not need a new max-height.
   `min-height: 0` on the child or the row refuses to go below its content. */
body.skin-majesty .we-slotwrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 180ms steps(6, end);
}
body.skin-majesty .we-slotwrap.is-open { grid-template-rows: 1fr; }
body.skin-majesty .we-slotwrap > .we-panel {
  min-height: 0;
  /* the collapsing row needs this or the content spills out of a shut
     panel -- but it also outranks the `overflow-y` below, which is how nine
     pixels of the stats row came to be clipped with no scrollbar to show for
     it. Hidden while shut, scrollable once open. */
  overflow: hidden;
}
body.skin-majesty .we-slotwrap.is-open > .we-panel { overflow: hidden auto; }

@media (prefers-reduced-motion: reduce) {
  body.skin-majesty .we-slotwrap { transition: none; }
}

/* THE PANEL DRAWS NOTHING. It is inside the card, so the card's nine-slice
   is already its frame; a second one here would be the frame-inside-a-frame
   this skin keeps removing, and it is what the seam was made of. */
body.skin-majesty .we-panel {
  border: 0;
  background: none;
  image-rendering: pixelated;
  color: var(--sk-ink);
  padding: 6px 0 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  /* A STARTING CAP ONLY -- world-equip.js `fit()` measures the real budget
     when the panel opens and overwrites this inline. Two comments used to sit
     here, each claiming to have DERIVED this number from the header, the
     ribbon strip and the gaps, and each disagreeing with the other. It was
     330 first, which dropped the stats row below the fold; corrected to 320,
     which on a 780-high window handed the panel 210px for 219px of content.
     A constant here is a claim about heights this rule cannot see. It stays
     only to stop a flash of full-height content before the measurement. */
  max-height: calc(100vh - var(--sk-foot) - 320px);
}
body.skin-majesty .we-panel[hidden] { display: none; }

body.skin-majesty .we-doll {
  display: grid;
  grid-template-columns: repeat(3, var(--sk-slot, 46px));
  gap: 5px;
  justify-content: center;
}

/* A SLOT IS A HOLE YOU DROP INTO (#2020, 2026-08-30).
   ──────────────────────────────────────────────────────────────────────
   Wray: "they are slots, so dragging the buttons from the bag over should
   slot into a bigger space not just change text on a button, should feel
   like your dropping it in that hole ... i dont like the hero slots are
   rectangulat make them same shape or larger like a slot".

   Three things follow, and the first two are why the old one felt wrong:

   SQUARE, AND BIGGER THAN THE CELL IT RECEIVES. 52px against the bag's 40.
   A rectangle reads as a field with a label; a square that is larger than
   the thing being dragged reads as somewhere it fits.

   THE ITEM ARRIVES AS ITSELF. What lands is the same sprite you picked up,
   sitting in the recess. Nothing about a caption changing says "dropped
   in"; a picture appearing in a hole does.

   THE NAME IS A WATERMARK, behind the sprite and covered by it. An empty
   slot has to say what it is for; a full one has to say what is in it, and
   the two facts do not need to share the space.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .we-slot,
body.skin-majesty .we-build {
  position: relative;
  width: var(--sk-slot, 46px);
  height: calc(var(--sk-slot, 46px) * var(--sk-card-ar, 1));
  box-sizing: border-box;
  /* no padding: the nine-slice border IS the inset, and 4px more on top of
     8px of border left the sprite under half the slot */
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-style: solid;
  border-image-source: url('/assets/ui/panel-well.png?v=2026-08-29a');
  border-image-slice: 4 fill;
  border-image-width: 8px;
  border-width: 8px;
  border-image-repeat: repeat;
  background: none;
  image-rendering: pixelated;
  color: var(--sk-ink);
  cursor: pointer;
  overflow: hidden;
}

/* THE WATERMARK IS A GLYPH. Wray: "slots in the hero card could be icons
   instead of text". A four-letter abbreviation in a 36px hole was already
   fighting for room and had to be shortened once; a drawn helm says the same
   thing at any size and in any language. slot-icons.png is a colour row over
   a grey row like every other sheet here, and the watermark uses the GREY
   one: it is a hint about an empty hole, not a thing to look at. */
body.skin-majesty .we-slot-k {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  background-image: url('/assets/ui/slot-icons.png?v=2026-08-30a');
  background-size: 96px 32px;
  background-position-y: -16px;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  opacity: .55;
  pointer-events: none;
}
/* FILLED MEANS THE GLYPH GOES. Wray, 2026-08-30: "when slotting the items in
   to equip the empty slot icon needs to be replaced with what we drag into
   it." Behind a sprite with any transparency the helm still showed through
   and the slot read as two things at once. */
body.skin-majesty .we-slot.is-full .we-slot-k,
body.skin-majesty .we-build.is-full .we-slot-k { display: none; }

/* 2A: THE SPRITE ALONE, ruled 2026-08-30.
   ──────────────────────────────────────────────────────────────────────
   A gold-over/dark-under band was drawn on filled slots to echo the card's
   anatomy, on the theory that a navy item on a navy ground has no edge. Wray
   picked A over it: with real cropped icons the item already fills the hole
   and reads on its own, so the band was solving a problem the icons had
   already solved and adding furniture to every occupied slot to do it.
   Deleted rather than left behind a flag. */

/* ══════════════════════════════════════════════════════════════════════
   A SOCKET (3A rotated + 4D), ruled 2026-08-30
   ══════════════════════════════════════════════════════════════════════
   Wray's ruling on what a socket IS came first: "socketing is adding a spell
   card into an open socket slot on the gear." So it hangs off a piece of
   equipment, not off the cat, and its home is an item's detail card rather
   than the hero panel.

   He picked A -- the same well art as a slot, just small -- and then solved
   the problem with picking A: "if we can rotate it as a diamond that solves
   the same shape but two mechanics visual disturbance." One art, two
   mechanics, and nobody mistakes a socket for a tiny item slot.

   HIS OWN CAVEAT, ANSWERED. "maybe the diamond wont work if the sprite clips
   over it." It would have. Rotating a square 45 degrees grows its bounding
   box by root two, so an 18px diamond needs a 26px box or its corners are cut
   off by the row around it -- and the space left INSIDE for the sigil is the
   inscribed square, 18/root two, under 13px. Both are handled here rather
   than discovered later:

     - `.ws-socket` is the outer box and carries the size. The rotation lives
       on `::before`, which draws the frame, so the box itself never rotates
       and never needs a bigger footprint than it claims.
     - The sigil sits in `.ws-socket-g` unrotated, inside the inscribed
       square. Counter-rotating a rotated child would work too and would blur
       a pixel sigil twice; not rotating it at all is free.

   Nothing consumes this yet -- there is no socket column on `items` and no
   socket UI -- so it is a spec in CSS rather than a feature. It is here so
   the decision is written down where the thing gets built, and so the
   variants page can show the resolved version. */
body.skin-majesty .ws-socket {
  --sock: 22px;
  position: relative;
  width: calc(var(--sock) * 1.415);
  height: calc(var(--sock) * 1.415);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  image-rendering: pixelated;
}

/* the frame, and the only thing that turns */
body.skin-majesty .ws-socket::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--sock);
  height: var(--sock);
  margin: calc(var(--sock) / -2) 0 0 calc(var(--sock) / -2);
  transform: rotate(45deg);
  border-style: solid;
  border-width: 4px;
  border-image-source: url('/assets/ui/panel-well.png?v=2026-08-29a');
  /* NO `fill`, for the same reason an empty slot lost it: the art's middle is
     a lit plate and a socket that looks like a button is the thing Wray
     called out. The dark ground below is the hole. */
  border-image-slice: 4;
  border-image-width: 4px;
  background-color: var(--sk-hole, #0b0f16);
  background-clip: padding-box;
}

/* 4D: THE SIGIL, NOT A COLOUR. A socketed card is a spell, and a flat house
   colour says only "something is in here" -- the sigil says which. It is held
   to the inscribed square so it never crosses the rotated frame. */
body.skin-majesty .ws-socket-g {
  position: relative;
  z-index: 1;
  width: calc(var(--sock) * 0.62);
  height: calc(var(--sock) * 0.62);
  background-image: url('/assets/ui/slot-icons.png?v=2026-08-30a');
  background-size: 96px 32px;
  background-repeat: no-repeat;
  image-rendering: pixelated;
}
body.skin-majesty .ws-socket.is-empty .ws-socket-g { display: none; }

/* 1D: AN EMPTY SLOT IS A HOLE, ruled 2026-08-30.
   ──────────────────────────────────────────────────────────────────────
   Wray picked D -- the faint gold rule -- and named what was wrong with all
   four: "the centre of the socket needs to look empty, right now it looks
   like a button."

   He is describing the `fill` keyword. `border-image-slice: 4 fill` paints
   the art's middle slice across the centre, and panel-well's middle is a lit
   plate, so every empty slot was a raised button wearing a carved frame.
   Dropping `fill` leaves the frame and opens the middle, and an explicit dark
   ground behind it makes that opening read as depth rather than as a hole in
   the page. `background-clip: padding-box` keeps the ground inside the frame
   instead of under it.

   FILLED SLOTS KEEP THEIR FILL. An icon sitting on nothing would float; the
   plate is what it sits on. So this is `.is-empty` only, and the two states
   differ in more than their contents -- which is the fastest empty/full tell
   on the panel and costs nothing to draw.

   The gold rule is D itself: a hairline inside the well saying "put something
   here" without the glyph having to shout. */
body.skin-majesty .we-slot.is-empty,
body.skin-majesty .we-build.is-empty {
  border-image-slice: 4;
  background-color: var(--sk-hole, #0b0f16);
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1px rgba(202, 169, 130, .28);
}

/* an empty slot lights its glyph on hover, so it says what it wants */
body.skin-majesty .we-slot:hover .we-slot-k,
body.skin-majesty .we-build:hover .we-slot-k { background-position-y: 0; opacity: .9; }

/* THE SPRITE FILLS THE HOLE. Wray, 2026-08-30: "the icons for items themselfs
   are quite small and can be larger to fill the button window." They were
   small for a reason worth naming rather than repeating: a slot spends most
   of itself on chrome. A 46px slot has an 8px nine-slice border on each side
   and 4px of padding inside that, leaving 22px of the 46 for the icon --
   under half the box, and it read as a stamp floating in a well.

   It stays in normal flow. The first attempt at this absolutely positioned
   the sprite with `inset: 4px` and `width: auto`, which does the OPPOSITE of
   what it reads like: an abspos replaced element with auto width uses its
   INTRINSIC size and hands the leftover room to its margins, so a 32px sprite
   would have stayed 32px and the insets done nothing. A flex child with
   `width: 100%` genuinely fills, and `contain` keeps the aspect. */
body.skin-majesty .we-slot img,
body.skin-majesty .we-build img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: contain;
  image-rendering: pixelated;
  pointer-events: none;
}

/* EQUIPPED BUT ARTLESS. The slot has something in it and no sprite to show
   for it, so it says so rather than looking empty -- an empty-looking slot
   that is full is a lie about your own kit. */
body.skin-majesty .we-slot.is-artless {
  box-shadow: inset 0 0 0 2px var(--sk-gold);
}
body.skin-majesty .we-slot.is-artless .we-slot-k {
  background-position-y: 0;   /* lit: something IS in here, art or not */
  opacity: 1;
}

/* AN EMPTY SLOT SAYS NOTHING LOUDLY. Six of them all announcing themselves
   is six pieces of noise; a quiet recess is legible as empty on sight. */
body.skin-majesty .we-slot.is-empty { opacity: .72; }

/* the drop target, while something is over it. The lit plate is the only
   moment a slot should be brighter than what it holds. */
body.skin-majesty .we-slot.is-over,
body.skin-majesty .we-build.is-over {
  border-image-source: url('/assets/ui/panel-lit.png?v=2026-08-29a');
  opacity: 1;
}

body.skin-majesty .we-rule {
  height: 2px;
  background: rgba(216, 166, 87, .3);
  margin: 2px 4px;
}

body.skin-majesty .we-builds {
  display: grid;
  grid-template-columns: repeat(3, var(--sk-slot, 46px));
  gap: 5px;
  justify-content: center;
}
body.skin-majesty .we-build { cursor: default; }

/* THE STATS ARE A FOOTNOTE, and that is the whole argument for where they
   live: they are a consequence of what is in the slots above, so they are a
   line under them rather than a page of their own. */
/* A GRID, NOT A WRAPPED RUN-ON. Five stats in a flex-wrap reflowed to
   whatever the width allowed, so they read as one long sentence --
   "ATK25HP450SPD20DEF20LUCK15+5" -- with the second line starting under
   nothing in particular. Numbers people compare want columns.

   Three columns, because five stats in three columns is two tidy rows with
   the odd one out at the start of the second, and because two columns makes
   the card taller for no gain. `minmax(0, 1fr)` so a four-digit HP cannot
   push its neighbour out of the card. */
body.skin-majesty .we-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px 6px;
  padding: 6px 4px 2px;
  border-top: 1px solid rgba(202, 169, 130, .18);
  margin-top: 2px;
}
/* the label sits ABOVE its number rather than beside it: at 6px the label is
   a caption, and a caption on the same line as a 15px number reads as part of
   the number */
/* THE LABEL IS A ROW OF ITS OWN, THE VALUE AND ITS DELTA SHARE ONE.
   A plain column put all three children on separate lines, so `+5` dropped
   below LUCK and the block grew a ragged third row. The delta only means
   anything touching the number it modifies. */
body.skin-majesty .we-stat {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  align-items: baseline;
  column-gap: 3px;
  min-width: 0;
}
body.skin-majesty .we-stat b { grid-column: 1 / -1; }
body.skin-majesty .we-stat b {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 6px;
  font-weight: 400;
  color: var(--sk-ink-dim);
}
body.skin-majesty .we-stat i {
  font-style: normal;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  line-height: 1;
  color: var(--sk-gold-hi);
  font-variant-numeric: tabular-nums;
}

/* WHAT THE GEAR IS ADDING, beside the total rather than instead of it: the
   total is what you fight with, the delta is why it is that number. Green
   because it is a gain; red when a piece costs you something, which is a
   thing items in this game can do. */
body.skin-majesty .we-delta {
  text-decoration: none;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 13px;
  color: #6fbf73;
  font-variant-numeric: tabular-nums;
}
body.skin-majesty .we-delta.is-down { color: #d4636a; }

/* NO RESERVED LINE. It held 16px open for a message that is absent almost
   all the time, and those were the last pixels between the panel fitting and
   scrolling. It takes the room only when it has something to say. */
body.skin-majesty .we-say {
  padding: 0 4px;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 14px;
  color: var(--sk-ink-dim);
  min-height: 0;
}
body.skin-majesty .we-say:empty { display: none; }

/* THE GHOST follows the pointer and must never be under it, or
   elementFromPoint returns the ghost and no slot ever lights. */
.we-ghost {
  position: fixed;
  z-index: 9500;
  pointer-events: none;
  transform: translate(-50%, -50%);
  width: var(--sk-cell, 40px);
  height: calc(var(--sk-cell, 40px) * var(--sk-card-ar, 1));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3px;
  box-sizing: border-box;
  background: rgba(29, 40, 54, .92);
  border: 2px solid #d8a657;
  image-rendering: pixelated;
}
.we-ghost img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  image-rendering: pixelated;
}
/* the fallback when the art will not load: something has to be under the
   pointer, and a name is better than an empty square */
.we-ghost.is-word {
  width: auto;
  height: auto;
  padding: 4px 8px;
  color: #e6d8bd;
  font: 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
}

/* a bag cell you can pick up says so */
body.skin-majesty .ww-cell[data-item-id],
body.skin-majesty .ww-cell[data-card-id] { cursor: grab; }


/* ══════════════════════════════════════════════════════════════════════
   THE BAG (#2197)
   ──────────────────────────────────────────────────────────────────────
   Uniform cells, one item each, icon only, details on hover. The shaped
   grid this replaces is deleted rather than left behind a class: two bag
   layouts in one stylesheet is one of them going stale.

   The cell is 40px because that is the size at which a bag can be scanned
   rather than read — the whole reason the details moved to a hover card.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ww-bagwrap {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  overflow-y: auto;
}

/* ⭐AS MANY COLUMNS AS FIT, NEVER MORE THAN SIX (#3031 P0-4). Six fixed
   64px columns are 411px, and on a phone the wrap is 274 with overflow
   hidden: the first and last columns sat outside it and could not be seen or
   tapped. `auto-fill` against a max-width counts the columns that fit; the
   cap is Wray's six (`--bc`, "6 items wide like albion") plus the well's 12px
   padding and 16px of room for a scrollbar, so a desktop bag still draws six
   and never seven. The width shrinks to the columns it drew (the wrap
   centres it), so the well's tiled sockets and the real cells stay in step.
   world-windows.js pads to the column count it reads back from here. */
body.skin-majesty .ww-bag1 {
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--sk-cell, 40px));
  box-sizing: border-box;
  max-width: min(100%, calc(var(--bc, 6) * (var(--sk-cell, 40px) + 3px) - 3px + 12px + 16px));
  gap: 3px;
  flex: 0 0 auto;
}

/* THE CELLS HAD TO STOP BEING NAVY ON NAVY. Wray, 2026-08-30: "not sure
   navy tiles for the items/cards on a navy background is the way forward."
   He is right and it is the cost of picking the navy frame: the well art and
   the screen art are the same value, so fifty-five cells dissolved into the
   page behind them. A darker ground inside each cell separates them without
   introducing a colour -- the cell becomes a hole in the panel rather than a
   tile on it, which is what it is. */
body.skin-majesty .ww-slotc {
  width: var(--sk-cell, 40px);
  height: calc(var(--sk-cell, 40px) * var(--sk-card-ar, 1));
  box-sizing: border-box;
  /* a positioning root for the pile count and the NEW mark (#2364, #2501) */
  position: relative;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* THE PACK'S OWN INVENTORY SLOT (Wray's Bag pick B, 2026-09-06: "take a
     look at the majesty pack it has inventory slot graphics that probably
     would suit us"). Item_Holder_hud.png, 40x40 native, a parchment square
     with a red-dashed inner border at 4..35 -- drawn 1:1 at the 40px cell
     (`--sk-cell`), stretched at any other size rather than sliced, because
     the dashes are the art and a nine-slice would repeat them. It replaces
     the navy panel-well that Wray was "really hung up on" as a tile on a
     navy ground; on the parchment it is the pack's inventory sheet.

     The cell is SHARED: the bag's loose items, its worn row, the Character
     window's paper-doll slots and its BUILD slots are all `.ww-slotc`, so
     one slot language reaches every place an item can sit -- which is how
     Albion draws them too. */
  border: 0;
  border-image: none;
  background: url('/assets/ui/slot-holder.png?v=2026-09-06a') 0 0 / 100% 100% no-repeat;
  /* the rarity is the rim, because rarity is what a bag is scanned for:
     a thin ring at the very edge, outside the dashes */
  box-shadow: inset 0 0 0 2px var(--r, transparent);
  image-rendering: pixelated;
  cursor: pointer;
  color: #3a2412;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
}
body.skin-majesty .ww-slotc:hover {
  border-image-source: url('/assets/ui/panel-lit.png?v=2026-08-29a');
}
/* AN ENCHANTED PIECE (C44, frame and glow; #3274). cellFor sets --r to the
   enchant colour, so the rim above already repeats it; this adds the soft
   glow inside the cell and round the art. The v2 slate bag draws its kit B
   rim instead (world-windows-v2.css). */
body.skin-majesty .ww-slotc.is-ench {
  box-shadow: inset 0 0 0 2px var(--r, transparent), inset 0 0 12px color-mix(in srgb, var(--ench, transparent) 45%, transparent);
}
body.skin-majesty .ww-slotc.is-ench > img {
  filter: drop-shadow(0 0 1.5px var(--ench)) drop-shadow(0 0 4px var(--ench));
}

/* PAST THE CARRY LINE. Dimmed rather than hidden: the limit is not live,
   and hiding things to imply that it is would be a promise the game does
   not keep yet. */
body.skin-majesty .ww-slotc.is-over { opacity: .45; }

/* ── A PILE IS ONE CELL (#2364) ──────────────────────────────────────
   The count sits bottom-right, where Albion draws its stack counts, in the
   heading face at the smallest size it still reads. Dark ground on the
   parchment socket so it is legible over any sprite. */
body.skin-majesty .ww-slotc .ww-stackn {
  position: absolute;
  right: 2px;
  bottom: 2px;
  z-index: 2;
  padding: 2px 2px 1px;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  font-weight: 400;
  line-height: 1;
  color: #f3e9d6;
  background: #2a1a10;
  box-shadow: 0 0 0 1px #5a4328;
  pointer-events: none;
}
/* what arrived since the bag was last opened (#2501): the same red the
   journal's mark uses, so "there is something here" reads the same way in
   both places */
body.skin-majesty .ww-slotc .ww-new {
  position: absolute;
  left: 2px;
  top: 2px;
  z-index: 2;
  padding: 2px 2px 1px;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  font-weight: 400;
  line-height: 1;
  color: #fff;
  background: #e03c31;
  box-shadow: 0 0 0 1px #2a1a10;
  pointer-events: none;
}

/* ── THE COUNT ON THE BAG DOOR (#2501) ───────────────────────────────
   Painted by world-shell's paintDoorPips onto whichever tile carries the
   Bag icon: the rail's .ws-tile or the Albion row's .an-tile. Both need to
   be a positioning root for it, and neither was. */
body.skin-majesty .ws-tile,
body.skin-majesty .an-tile { position: relative; }
body.skin-majesty .ws-doorpip {
  position: absolute;
  top: 2px;
  right: 2px;
  z-index: 2;
  min-width: 7px;
  padding: 2px 3px 1px;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  font-weight: 400;
  line-height: 1;
  text-align: center;
  color: #fff;
  background: #e03c31;
  box-shadow: 0 0 0 2px #2a1a10;
  pointer-events: none;
}

/* Same as the hero slots: a 40px cell keeps 6px of border a side and 3px of
   padding, so the icon was living in 22px of 40. It gets the padding box. */
body.skin-majesty .ww-slotc img {
  /* ⭐62, WAS 78 (Wray, 2026-09-08: "The icons inside the frame are slightly
     too big. Do we have an option to make the icons smaller?"). The dashed
     border is 4..35 of the 40px art, so 78% put the sprite right on the dashes
     at every size; 62 leaves the frame its own room and the cell reads as a
     socket holding a thing rather than a thing with a frame drawn round it. */
  width: 62%;
  height: 62%;
  object-fit: contain;
  image-rendering: pixelated;
  pointer-events: none;      /* the drag reads the BUTTON, not the sprite */
}

body.skin-majesty .ww-slotc .ww-abbr {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  pointer-events: none;
}

/* THE HOVER CARD. On parchment, like every other readable surface in this
   skin, and never under the pointer — a tooltip that can be hovered is a
   tooltip that flickers. */
.ww-hover {
  position: fixed;
  z-index: 9400;
  pointer-events: none;
  max-width: 240px;
  padding: 8px 10px;
  background: #e4d5aa;
  color: #3a2a18;
  border: 2px solid #5a4328;
  box-shadow: 0 4px 0 rgba(0, 0, 0, .45);
  font-family: 'VT323', ui-monospace, monospace;
  font-size: 16px;
  line-height: 1.25;
  image-rendering: pixelated;
}
.ww-hover b { display: block; font-weight: 400; font-size: 18px; }
.ww-hover > i {
  display: block;
  font-style: normal;
  font-size: 18px;
  color: #5a4328;
  margin-bottom: 4px;
}
.ww-hover .ww-hover-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 10px;
  margin: 4px 0 2px;
}
.ww-hover .ww-hover-stats span { display: flex; gap: 4px; }
.ww-hover .ww-hover-stats b {
  display: inline;
  font-size: 12px;
  color: #5a4328;
}
.ww-hover .ww-hover-stats i {
  font-style: normal;
  color: #2f6b34;
}
.ww-hover p {
  margin: 4px 0 0;
  font-size: 14px;
  color: #5a452c;
}

/* ══════════════════════════════════════════════════════════════════════
   THE POP-UP WINDOW (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray, 2026-08-29: "every other page like spellbook, opens its own
   designed window", replacing "the old glass chrome".

   Every door opens THIS, so it is the one place that changes all of them.
   world-shell.js builds `.ws-sheet` with a title row, a tab strip and a
   body that frames the real page; none of that markup moves here.

   WHAT CHANGES, AND WHY IT IS A WINDOW RATHER THAN A PAGE
   The sheet covered the picture edge to edge, which reads as navigating
   away — the website idiom the project is trying to leave. Inset with a
   drawn frame and the room visible around it, the same sheet reads as a
   window opened over a place you are still standing in. That is the MMO
   move, and it is four properties rather than a rewrite.

   The tabs become Majesty's page ribbons, hanging down over the window's
   top edge the way they are drawn. They were already a tablist with
   aria-current doing the telling, so this is paint on working behaviour.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ws-sheet {
  /* the tabs hang outside the right edge, so this is their origin and the
     window must not clip them */
  position: fixed;
  overflow: visible;
  /* SMALLER, AND CENTRED ON THE PICTURE. Wray: "where we can, we want them to
     be smaller so you can see more of the background of the screen around ...
     perhaps you want to use an item whilst you're seeing what's going on".
     A window that covers the room is a page with a frame drawn on it. */
  /* THE ASK, CLAMPED TO THE PICTURE (#2557). Declared HERE and not on the
     body: a custom property is resolved where it is declared, so a derived
     value on an ancestor would bake in the ancestor's ask and ignore the
     door's. 8px of room each side. Vertically the window is centred in the
     BAND above the foot and the tray, not in the whole picture, so a tall
     list never sits on the bar of four. */
  /* ⭐THE BAND RESERVED THE TRAY ON BOTH SIDES, AND NERM ONLY SITS ON ONE
     (Wray, 2026-09-08: "Bag window still seems a bit short it could extend
     lower probably to the top of where the zoom buttons are").
     
     Measured at 1920x1080: the sheet ended at 959 and the foot bar starts at
     1042 -- 83px of nothing. The band subtracts `--sk-tray` (50px) as well as
     the foot, and the tray is where Nerm's speech bubble lives: measured at
     12,992, 320x46. That is the BOTTOM-LEFT corner. A right-docked sheet
     starts at x1392 and can never touch it.
     
     So the tray comes off the LEFT side only, below. A right-docked window now
     runs to 8px above the foot, which is what he asked for. */
  --sk-win-band: calc(var(--fh, 100vh) - var(--sk-foot));
  /* the ask, resolved HERE so a per-door share is in scope; the lab's inline
     `--sk-win-w` on <body> overrides it when a slider has been dragged */
  --sk-win-wder: max(var(--sk-win-wmin), calc(var(--fw, 100vw) * var(--sk-win-wpct)));
  --sk-win-hder: max(var(--sk-win-hmin), calc(var(--fh, 100vh) * var(--sk-win-hpct)));
  --sk-win-cw: min(var(--sk-win-w, var(--sk-win-wder)), calc(var(--fw, 100vw) - 16px));
  /* THE DOCK (#2557, 2026-09-06). Not centred any more: a window hugs an
     edge of the picture and hangs from the top, the way Albion's do, so two
     can be open with the room between them. The default edge is the RIGHT
     (what is yours: the bag, your cards); `[data-side='left']` below is the
     world's side. 84 clears the door row in the top-right corner (12 edge +
     44 tile + the ribbon's 20px overhang + breathing room); the left side
     starts at 16 and covers the plate, which is what Albion's bank does. */
  /* ⭐96, WAS 84 (Wray, 2026-09-08: "move the page down so the navigation
     buttons in the top right are still viewable"). Measured: the nav row ends
     at y56 and the banner's top sat at y54 -- a 2px overlap, caused by the
     banner nudge earlier the same day. The window now starts low enough that
     the banner clears the row by 16px. */
  --sk-win-top: 96px;
  --sk-win-ch: min(var(--sk-win-h, var(--sk-win-hder)), calc(var(--sk-win-band) - var(--sk-win-top) - 8px));
  left: calc(var(--fx, 0px) + var(--fw, 100vw) - var(--sk-win-cw) - 8px);
  top: calc(var(--fy, 0px) + var(--sk-win-top));
  /* The shared default. A door that wants its own shape sets the ASK in a
     `.ws-sheet[data-door='...'], .ws-tabs[data-door='...']` rule (both, or
     the strips lay out for a window the sheet does not draw) and a door
     nobody has tuned opens the way it always did. */
  width: var(--sk-win-cw);
  height: var(--sk-win-ch);
  /* PARCHMENT. Wray: "I was expecting more to use the parchment from the
     medieval pack". The screen frame stays as the border; the ground inside
     it is paper, which is what makes it read as a page rather than a slab. */
  /* STRETCHED, NOT TILED. paper.png is a 96px page SCRAP with decorated
     edges rather than a seamless texture, so repeating it draws its own
     border every 96px and the window reads as graph paper. Stretched, the
     grain is wrong by a factor nobody can name and the seams are gone. */
  /* LONGHANDS AND A SOLID FALLBACK. The shorthand plus background-clip left
     the window painting nothing but its colour, and a window that depends on
     an image decoding is a window that can be invisible. The colour is the
     floor: whatever happens to the art, there is always a ground. */
  /* ONE SURFACE. Wray, 2026-08-29: "right now we have menus with a window
     and parchment inside its too much."

     It was literally two: `screen.png` nine-sliced as the window, and then
     paper.png as a second sheet inside it, so every door opened a frame
     containing a page containing the actual content. Majesty's own menu
     screens are one or the other — the inventory sheet in the pack IS a
     torn parchment with tabs down its edge, and the settings screen IS a
     dark panel with a banner over it. Never both stacked.

     The parchment wins, because it is the one the tabs are drawn to hang
     off. paper.png is a 96px scrap with ragged decorated edges, so it is
     nine-sliced rather than stretched: at 16/x2 the torn edge stays torn at
     any window size and only the middle of the page grows. */
  /* ⭐THE MIDDLE IS A FLAT COLOUR, NOT A STRETCHED TEXTURE (Wray, 2026-09-08:
     "i see these really weird like margin lines on the parchment").
     
     ⛔THE CAUSE IS `fill` PLUS `pixelated`, NOT THE ART. `border-image-slice:
     16 fill` painted the scrap's 64x64 middle across the content box -- 456px
     at this window, a 7.1x stretch. With `image-rendering: pixelated` a
     non-integer stretch cannot give every source pixel the same width, so they
     alternate 7px and 8px and the near-uniform paper bands. Measured by
     scanning a clean row: irregular jumps of 10-15 at x+139, x+153, x+224,
     x+500 -- faint, regular enough to read as ruled lines, and worse at any
     zoom that makes the ratio uglier.
     
     Dropping `fill` stops the middle being drawn from the texture at all; the
     torn edges still come from it, still at 16/x2, still crisp. The interior
     becomes a flat colour sampled from the scrap's own middle (214,185,146),
     which it was within a shade of everywhere anyway. That also finally makes
     good on the "solid fallback" this block has claimed since it was written:
     with the image undecoded there is now actually a ground. */
  background: none;
  /* ⛔#d5b68c, NOT #d6b992 -- MATCHED TO THE BORDER'S INNER EDGE, not to the
     texture's middle. The paper has a vignette, so its centre (214,185,146) is
     a shade lighter than where the nine-sliced edge hands over (213,182,140).
     Sampling the middle put a 3/6-unit step at exactly 32px in, running the
     full height of the window on all four sides -- Wray's "slight trace line
     around the parchment frame", still there after the clip fix because the
     clip was a different fault. Found by scanning a row across the frame, not
     by looking. */
  background-color: #d5b68c;
  /* ⛔CLIPPED TO THE PADDING BOX, or the colour fills the whole border box and
     shows THROUGH the torn edge's transparent notches -- a straight edge
     sitting behind a ragged one, which is Wray's "its like double layered so
     the cut edges have a straight edge behind it" (2026-09-08). The flat
     colour exists to stop the middle banding; it has no business under the
     art. */
  background-clip: padding-box;
  border-style: solid;
  border-image-source: url('/assets/ui/paper.png?v=2026-08-29a');
  /* ⛔28, NOT 16 — THE TORN BITE MUST NOT BE IN THE REPEATING STRIP.
     Wray, 2026-09-08, with a photo: a regular row of tabs down the left edge.
     Read off paper.png, the left outline's x-position per row: it sits at x=0
     everywhere EXCEPT y0-4, y9-11, y18-27 and y87-95 — the corners, and two
     bites out of the edge. `slice: 16` left the y18-27 bite inside the
     left/right EDGE strip, and `round` tiled that strip ~5 times down a 950px
     window, so one torn bite became five evenly spaced tabs. Even spacing is
     what makes it read as a manufactured seam instead of torn paper.
     At 28 the bites fall in the CORNER, which is drawn once; the edge strip is
     then y28..67, which the same scan shows is x=0 for every row — nothing
     left to repeat. */
  border-image-slice: 28;
  border-image-width: 28px;
  border-width: 28px;
  /* ⛔`round`, NOT `stretch`, AND THIS IS WHERE THE LINES WERE. Stretching a
     16px edge slice across 456px of content box is a 28x scale; with
     `image-rendering: pixelated` a non-integer scale gives source pixels
     alternating widths, and on near-uniform parchment that reads as soft
     vertical stripes down the margins -- exactly what Wray was seeing.
     Magnified at the bottom border it is unmistakable.
     
     `round` tiles the 16px slice instead, scaling it so a whole number fits,
     so every tile is drawn at the same size and the error is per-tile and
     tiny. A torn edge is a repeating texture anyway; it was never a single
     motif that had to span the window. */
  /* ⛔THE NOTCH MUST NOT BE IN THE REPEATING STRIP (Wray, 2026-09-08, with a
     photo: a regular row of tabs down the left edge).
     
     Read off paper.png at x=0: the outline runs dark EXCEPT y18..y27, which is
     transparent -- a torn notch. With `border-image-slice: 16` that notch sits
     inside the left/right EDGE strip (source y16..79), and `round` tiles that
     strip about five times down a 950px window, so the notch repeats five
     times at an even pitch. Even spacing is what makes it read as a
     manufactured seam instead of torn paper. `stretch` is worse: it turns one
     10px notch into a 140px gap.
     
     Slicing at 28 puts the notch in the CORNER, which is drawn once. The edge
     strip is then source y28..67, which the same scan shows is solid dark --
     nothing left to repeat or stretch, so the sides are clean at any height
     and the corners keep their tear. */
  border-image-repeat: round;
  image-rendering: pixelated;
  color: #2e2113;                 /* ink on paper, not cream on navy */
  padding: 0;
  box-shadow: 0 8px 0 rgba(0, 0, 0, .45);
}

/* THE ROOM DOES NOT DIM. Wray, 2026-08-29: "they shouldn't dim the rest of
   the screen they should just appear in the space."

   The dim was a 100vmax spread shadow, which was the fix for an earlier bug
   where a `::before` overlay painted on top of the window's own parchment
   instead of behind it. Both are gone now: the window is a thing opened in a
   room you are still standing in, and dimming the room is the website idiom
   of navigating away, which is the thing this whole skin is leaving.

   The drop shadow stays. It is what lifts the window off the picture now that
   nothing else separates the two. */
body.skin-majesty .ws-sheet {
  /* NO RING. Wray, 2026-08-29: "it has a navy border, not needed when the
     parchment is the canvas." The 3px ring was what was left of the dim after
     the window stopped being modal — a spread shadow shrunk rather than
     removed. The torn edge of the page is the edge; drawing a second one
     around it is the same mistake as the frame-inside-a-frame, just thinner.
     The drop shadow stays: it is what lifts the page off the picture. */
  box-shadow: 0 8px 0 rgba(0, 0, 0, .45);
}

body.skin-majesty .ws-sheet-top {
  border-bottom: 0;
  padding: 2px 0 4px;
  align-items: flex-start;
  /* CLOSE BACK TO THE RIGHT. This row was `title | close` and the
     space-between pushed CLOSE to the far edge. The title moved onto its
     banner and out of the flow, leaving CLOSE the only child — so it fell
     back to the left corner, straight into the left ribbon strip. */
  justify-content: flex-end;
}

/* THE DROP CAP, GOLD ON THE RED BANNER. Wray, 2026-08-30: "the highlight
   gold as the large first letter in the title followed by parchment text
   colour." On the parchment sheets the initial is red on cream; here it is
   the reverse, because the banner is the red. ::first-letter rather than a
   span, so the shell keeps writing a plain string into the title and nothing
   has to know about it. */
body.skin-majesty .ws-sheet-title::first-letter {
  color: #d8a657;
  font-size: 1.3em;
}

/* THE TITLE RIDES A BANNER OVER THE TOP EDGE. Wray: "also a title ribbon",
   pointing at Majesty's settings screen, where the name of the panel hangs on
   a swallowtail banner half off its top edge rather than sitting inside it as
   a heading. title-banner.png is the pack's ribbon5, recoloured gold — always
   gold, because a title is never the unselected one.

   Three-sliced so the tails stay crisp at any title length, and pulled up out
   of the page so it reads as fixed ON the parchment rather than printed on
   it. `position: absolute` against the sheet, which is already the origin for
   the side ribbons. */
body.skin-majesty .ws-sheet-title {
  position: absolute;
  left: 50%;
  top: -26px;
  translate: -50% 0;
  min-width: 200px;
  max-width: 70%;
  height: 40px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px 8px;
  border-style: solid;
  /* RED. Navy was tried and Wray called it: "doesnt look good in the navy so
     lets go with red". Red is also the canon unselected-ribbon colour, which
     is right for a title — a title is not a thing you choose. */
  border-image-source: url('/assets/ui/title-banner-red.png?v=2026-08-30b');
  /* 89x20 art: 16px tails each side, the plank between. The 4px bottom slice
     is the banner's own shadow lip, which is why the padding above leaves
     room for it rather than centring the text in the whole 40px. */
  border-image-slice: 0 16 0 16 fill;
  border-image-width: 0 32px;
  border-width: 0 32px;
  border-image-repeat: stretch;
  image-rendering: pixelated;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  z-index: 3;
  /* THE TYPE CAME WITH IT rather than being left behind in a rule nothing
     matches. 22px not 32: the old size was set for a heading with the whole
     width of the page, and a 40px banner is not that. Ink stays dark — it is
     on gold now instead of on paper, and both want the same dark. */
  font-family: var(--sk-tome);
  font-size: 22px;
  line-height: 1;
  letter-spacing: 0;
  color: #e8d5a9;              /* parchment text on red */
  text-shadow: none;
  text-transform: none;
}

/* CLOSE is a `.wr-plank`, which world-rail.css already draws. It only needs
   to stop being wood and start being gold. */
/* CLOSE IS A COZY PLATE. Wray, 2026-08-30: "make the close a cozy button."
   It was the last control in a window still drawing a flat bordered box while
   every other button in the skin is a nine-sliced plate. Same slice and scale
   as the chat bar and the map tools, so they are visibly one object at one
   size rather than three things that happen to be brown. */
/* CLOSE IS A BOOKMARK, NOT A BUTTON. Wray, 2026-09-03: "The close button can
   be replaced with ribbon-open / ribbon-closed in the top right with an x or
   close written horizontal."

   So the window's way out becomes the same object as the journal's marks: a
   ribbon hung over the sheet's top edge, red while it waits and gold under
   the hand. The nine-slice is `.wt-mark`'s, unchanged, because that recipe is
   already proven against this art -- `fill` is required or only the cap and
   the notch draw and the ribbon renders as two slivers.

   WHAT WAS HERE BEFORE was a rule at war with itself: it set up a nine-sliced
   `btn-idle` plate and then, eight lines later, overrode it with
   `border: 3px solid` and a flat navy fill, which silently threw the
   border-image away. Its own comment still described the plate. Neither the
   plate nor the box survives.

   THE LABEL IS "X", HORIZONTAL. world-shell.js keeps the accessible name
   "Close" on the button, so the control is still named for anyone who cannot
   see the glyph. The ribbon is 36px across and "CLOSE" at a legible bitmap
   size is not, which is why the X is the one that ships.

   PADDING-LEFT IS THE SAME CORRECTION AS THE HORIZONTAL RIBBONS, turned
   ninety degrees: the art's face is columns 6-14 of 15, so its centre is 2.5
   columns right of the element's. 36 x 2.5/15 = 6, applied as 12px of left
   padding because centred content moves by half of it. */
body.skin-majesty .ws-sheet-top .wr-plank {
  /* 30, NOT 36 (Wray, 2026-09-04): "The close button is too wide and its
     showing the start of the next horizonal ribbon theres a slither of
     pixels." The sliver was the art's own left columns: ribbon-closed.png is
     15px across and its first three columns are the mostly-transparent notch
     edge, so stretching 15 -> 36 blew those stray pixels into a visible
     strip that read as a neighbouring ribbon starting. 30 is the art at a
     whole x2 -- same as the journal marks, which never showed the sliver. */
  width: 30px;
  /* ⛔AND `min-width`, OR THE TAP FLOOR SILENTLY UNDOES IT. Wray reported the
     sliver again on 2026-09-08 -- "The X in the top right is too wide, so
     there is a slither of a second ribbon when it should be just one" -- and
     the 09-04 fix above was still here and still correct. What changed is
     that `body.skin-majesty .wr-plank` later grew `min-width: 44px` (#2490's
     tap floor), which beats a smaller `width` without touching it. Measured:
     computed width 44px with exactly one rule setting width, and that rule
     saying 30.

     The floor is for thumbs, so it is kept where thumbs are and dropped where
     a mouse is -- rather than negotiated away, which is what the block that
     introduced it says must not happen. */
  min-width: 30px;
  height: 48px;
  flex: 0 0 auto;
  box-sizing: border-box;
  /* ⭐ALIGNED TO THE TOP EDGE (Wray, 2026-09-08: "it needs aligning with the
     top edge"). It sat 4px BELOW the sheet's top; -14 puts its own top on the
     edge, the way the banner beside it sits. */
  margin: -14px 4px -16px 0;
  border-style: solid;
  border-image-source: url('/assets/ui/ribbon-closed.png?v=2026-08-29b');
  border-image-slice: 5 0 4 0 fill;
  border-image-width: 10px 0 8px 0;
  border-width: 10px 0 8px 0;
  border-image-repeat: repeat;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 8px 12px;      /* right onto the face, up off the notch */
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0;
  color: #f6ecd6;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
  cursor: pointer;
}

body.skin-majesty .ws-sheet-top .wr-plank:hover {
  border-image-source: url('/assets/ui/ribbon-open.png?v=2026-08-29b');
  background: none;
  color: #2e2113;             /* ink on gold, as everywhere else */
  text-shadow: none;
}

/* FOCUS IS A RING, NOT THE GOLD. `:focus-visible` was in the rule above and it
   made the closed state unreachable: world-shell.js:605 focuses this control
   the instant a sheet opens, so every window opened already showing its way
   out lit gold, and the red ribbon Wray asked for was never once on screen.
   Caught by measuring the computed art rather than by looking -- pale gold on
   a parchment page reads as an unfilled outline, which is what it looked like.

   Keyboard focus still has to be visible or the control is unusable without a
   mouse, so it keeps a ring. The ring is the affordance; the gold is the
   hover. */
body.skin-majesty .ws-sheet-top .wr-plank:focus-visible {
  outline: 2px solid var(--sk-gold-hi);
  outline-offset: 1px;
}


/* ── the tabs, as page ribbons ─────────────────────────────────────────
   Wray, 2026-08-29: "the ribbons need to live on the parchment and animate
   just like the source website showcases. ribbons expanding/shrinking" and
   "#A4343A should be the canon colour for unopened ribbons, the gold can be
   the highlighted."

   ON THE PARCHMENT, not over the frame: they were absolutely positioned
   against the sheet's padding box and hung across the drawn border, which
   put them on the wood rather than on the page.

   LENGTH IS A NINE-SLICE, NOT A SCALE. The art is 15x39: a decorative head
   (rows 0-4), a uniform middle (5-34) and a notched foot (35-38). Sliced
   `5 0 4 0` the middle REPEATS, so the ribbon can be any length with its
   pixels intact — where scaling a 39px image to 48 would land the dither
   between pixels and blur the one element that is meant to be crisp. That
   is what makes the expand animate honestly. */
/* THE WINDOW'S TABS HANG OFF BOTH EDGES.
   ──────────────────────────────────────────────────────────────────────
   They were vertical banners across the top, in vertical text, which is how
   the pack draws a BOOKMARK and not how it draws a tab strip. In Majesty's
   own inventory sheet the tabs are horizontal ribbons stacked down the outer
   edge of the page, and the selected one runs out further than the rest.

   Absolute, and outside the parchment: a tab that sat inside the page would
   be a label on it. Out on the edge it reads as an index you can grab. Same
   art as the rail, mirrored, so the notch points back into the page. */
/* LEFT IS WHAT THIS WINDOW IS; RIGHT IS WHAT ELSE YOU CAN REACH.
   Wray, 2026-08-29: "leaderboard partchment ribbons should be on the left
   like the screenshot and each ribbon sends you to a different leaderboard,
   then we use ribbons on the right like screenshot for extra menus within."
   That is how the pack's own inventory sheet reads, and it also settles what
   was becoming a queue of five tabs down one edge. */
/* BEHIND THE PARCHMENT, BOTH SIDES NOW. Wray, 2026-09-04: "left side
   navigation ribbons should be behind the parchment not ontop." The strip is
   a fixed SIBLING below the sheet (z 59 under 60), exactly the right strip's
   mechanism -- a child cannot paint behind its parent's background, which is
   why absolute-inside-the-sheet could only ever sit on top. Positioned
   against the same render rectangle the sheet is, so the two stay locked
   with no measuring: its RIGHT edge lands 14px inside the page's left edge,
   the same tuck the right strip wears. */
body.skin-majesty .ws-tabs {
  position: fixed;
  z-index: 59;
  /* the same derivation the sheet makes, on the strip itself (#2557) */
  /* ⭐THE BAND RESERVED THE TRAY ON BOTH SIDES, AND NERM ONLY SITS ON ONE
     (Wray, 2026-09-08: "Bag window still seems a bit short it could extend
     lower probably to the top of where the zoom buttons are").
     
     Measured at 1920x1080: the sheet ended at 959 and the foot bar starts at
     1042 -- 83px of nothing. The band subtracts `--sk-tray` (50px) as well as
     the foot, and the tray is where Nerm's speech bubble lives: measured at
     12,992, 320x46. That is the BOTTOM-LEFT corner. A right-docked sheet
     starts at x1392 and can never touch it.
     
     So the tray comes off the LEFT side only, below. A right-docked window now
     runs to 8px above the foot, which is what he asked for. */
  --sk-win-band: calc(var(--fh, 100vh) - var(--sk-foot));
  /* the ask, resolved HERE so a per-door share is in scope; the lab's inline
     `--sk-win-w` on <body> overrides it when a slider has been dragged */
  --sk-win-wder: max(var(--sk-win-wmin), calc(var(--fw, 100vw) * var(--sk-win-wpct)));
  --sk-win-hder: max(var(--sk-win-hmin), calc(var(--fh, 100vh) * var(--sk-win-hpct)));
  --sk-win-cw: min(var(--sk-win-w, var(--sk-win-wder)), calc(var(--fw, 100vw) - 16px));
  /* ⭐96, WAS 84 (Wray, 2026-09-08: "move the page down so the navigation
     buttons in the top right are still viewable"). Measured: the nav row ends
     at y56 and the banner's top sat at y54 -- a 2px overlap, caused by the
     banner nudge earlier the same day. The window now starts low enough that
     the banner clears the row by 16px. */
  --sk-win-top: 96px;
  --sk-win-ch: min(var(--sk-win-h, var(--sk-win-hder)), calc(var(--sk-win-band) - var(--sk-win-top) - 8px));
  left: auto;
  /* the docked sheet's left edge plus the 14px tuck: the picture's right
     edge, less the window, less the 8px margin (world-shell.js re-pins this
     inline every frame on a desktop; this is the no-JS and first-paint value) */
  right: calc(100vw - var(--fx, 0px) - var(--fw, 100vw) + var(--sk-win-cw) + 8px - 14px);
  top: calc(var(--fy, 0px) + var(--sk-win-top) + 44px);
  margin: 0;
  padding: 0;
  gap: 5px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;     /* they run out to the LEFT */
  overflow: visible;
  min-height: 0;
}
body.skin-majesty .ws-tabs[hidden] { display: none; }

/* THE RIGHT STRIP TUCKS UNDER THE PAGE, so it is not a child of it — a child
   cannot paint behind its parent's background, and `z-index: -1` puts it
   between the background and the content rather than behind both. It is a
   sibling now, positioned against the SAME render rectangle the sheet is
   centred on, so the two stay locked with no measuring and no resize
   listener. z-index 59 against the sheet's 60 is the whole trick. */
body.skin-majesty .ws-tabs.ws-tabs-r {
  position: fixed;
  z-index: 59;
  right: auto;
  /* the sheet's right edge, less the tuck */
  /* Tucked by the torn edge only. At 30px the parchment ate the first letter
     of every label -- "oday", "eason", "l time" -- which is behind the page in
     the wrong sense. 14 is the overhang the left strip uses, so the two edges
     match and the words survive. */
  left: calc(var(--fx, 0px) + var(--fw, 100vw) - 8px - 14px);
  /* BELOW THE CLOSE BUTTON. Level with the left strip the first tab ran under
     CLOSE, which sits in the window's top right — two controls in one corner,
     and the one on top is the one you press by accident. */
  top: calc(var(--fy, 0px) + var(--sk-win-top) + 78px);
  margin: 0;
  align-items: flex-start;
}
body.skin-majesty .ws-tabs.ws-tabs-r[hidden] { display: none; }

body.skin-majesty .ws-tabs:empty { display: none; }
/* under full bleed the right dock bleeds 28px off the edge (see the sheet's
   own rule), so its strip's first-paint value moves with it; the shell
   re-pins it from the measured rectangle every frame after */
body.skin-majesty.frame-fill-desktop .ws-tabs[data-side='right'] {
  right: calc(100vw - var(--fx, 0px) - var(--fw, 100vw) + var(--sk-win-cw) - 28px - 14px);
}

/* ON A PHONE THE STRIPS COME INSIDE (#2557 finding 3). Hanging ribbons need
   room beside the window, and a 374px sheet on a 390px screen has none: every
   tab on every door landed off-screen, measured at 390x664 on the Leaderboard,
   Store, Rooms and Bag. Below the breakpoint world-shell.css already collapses
   the rail at, both strips become a row along the sheet's top inside edge,
   scrolling sideways when the ladders do not fit, 44px tall because a thumb
   has to land on them (#2490). The sheet body is padded to clear one row when
   the left strip has tabs, and two when the right strip does too; a door with
   one tab draws no strip and pays nothing. */
@media (max-width: 900px) {
  body.skin-majesty .ws-tabs,
  body.skin-majesty .ws-tabs.ws-tabs-r {
    z-index: 61;
    /* from the DOCKED sheet's corner (#2557): on a phone the window is the
       picture less 16, so both sides land at fx + 8 and the row 10px in;
       world-shell.js re-pins this inline every frame, this is first paint */
    left: calc(var(--fx, 0px) + var(--fw, 100vw) - var(--sk-win-cw) - 8px + 10px);
    right: auto;
    width: calc(var(--sk-win-cw) - 20px);
    top: calc(var(--fy, 0px) + var(--sk-win-top) + 40px);
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
    gap: 6px;
    height: 44px;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
  }
  body.skin-majesty .ws-tabs.ws-tabs-r {
    top: calc(var(--fy, 0px) + var(--sk-win-top) + 88px);
  }
  /* the edge-keyed rules below this block are (0,3,1) and would otherwise
     re-widen the row's tabs to 104 and re-align it; the row is a row */
  body.skin-majesty .ws-tabs[data-edge] { align-items: center; }
  body.skin-majesty .ws-tabs[data-edge] .wr-plank { min-width: 88px; width: auto; }
  body.skin-majesty .ws-tabs .wr-plank {
    height: 44px;
    min-width: 88px;
    flex: 0 0 auto;
  }
  /* the strips are inserted BEFORE the sheet (world-shell.js:669-677), so a
     following-sibling combinator from strip to sheet is all this needs */
  body.skin-majesty .ws-tabs:not(.ws-tabs-r):not(:empty) ~ .ws-sheet .ws-sheet-body { padding-top: 44px; }
  /* `.ws-tabs.ws-tabs-r`, both classes: the one-row rule above carries two
     pseudo-classes, and a two-row rule with fewer parts LOST to it on
     specificity however late it came. Measured 2026-09-06: 44px applied,
     the right strip sat on the leaderboard's first row. */
  body.skin-majesty .ws-tabs.ws-tabs-r:not(:empty) ~ .ws-sheet .ws-sheet-body { padding-top: 92px; }
}

body.skin-majesty .ws-tabs .wr-plank {
  flex: 0 0 auto;
  width: 104px;
  height: 30px;
  box-sizing: border-box;
  border-style: solid;
  /* THE SAME TURNED PAGE MARKER AS THE RAIL. One ribbon in this UI, in two
     colours and two directions -- the side tab was a second ribbon language
     and Wray ruled it out.

     The notch always faces the page: on the left strip it points right into
     the parchment, on the right strip it is the mirror (see below). Four
     files rather than one `scaleX(-1)`, because a CSS flip turns the label
     with it. */
  /* THE TAB ART IS BACK FOR THE WINDOW PAGES. Wray, 2026-09-03: "The bag,
     store and leaderboard window pages all have navigation ribbons on the
     left, however the art used should be tab-on-left / tab-off-left."

     This reverses the 2026-08-29 note above for THESE strips only. The rail's
     doors keep the turned-page marker; a window's own pages are a file's tabs,
     which is a different thing to say and the pack draws it. The rail's ribbon
     stays the ribbon, so there is still one ribbon language -- there are now
     two objects, and they are different objects.

     44x12 art, measured: a 4px shaped cap on the LEFT (the edge that sticks
     out past the page) and a square right edge. At the 30px height that is
     2.5x, so the cap draws at 10px and the square edge at 3. */
  border-image-source: url('/assets/ui/tab-off-left.png?v=2026-09-03a');
  border-image-slice: 0 1 0 4 fill;
  border-image-width: 0 3px 0 10px;
  border-width: 0 3px 0 10px;
  border-image-repeat: stretch;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  /* RE-MEASURED FOR THE TAB ART, which is a different sheet to the ribbon
     and needs a different number. 44x12: face rows 1-8 (centre 5.0), art
     centre 6.0, so the offset is 1 of 12 rather than 2.5 of 15. At 30px that
     is 2.5, so the padding is 5. See "WHY EVERY RIBBON LABEL SAT LOW" above
     for why a bottom padding is the way it is applied.

     This rule covers BOTH strips: world-shell.js builds the right-hand one as
     `ws-tabs ws-tabs-r`, so it carries this class too and only its art is
     overridden below. */
  padding: 0 0 5px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  writing-mode: horizontal-tb;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 9px;
  letter-spacing: .06em;
  color: #f6ecd6;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
  white-space: nowrap;
  cursor: pointer;
  /* the run-out. Width only, stepped, so every frame is still pixel art and
     nothing above or below it moves. */
  transition: width 120ms steps(5, end);
}

body.skin-majesty .ws-tabs .wr-plank:hover { width: 122px; }

/* THE RUN-OUT IS BIGGER. Wray, 2026-08-30, on the gold: 104 -> 128 is 24px,
   which at a glance reads as a rendering wobble rather than a chosen tab. 156
   is unmistakably longer than its neighbours, which is the whole job of the
   animation. */
body.skin-majesty .ws-tabs .wr-plank[aria-current='page'] {
  border-image-source: url('/assets/ui/tab-on-left.png?v=2026-09-03a');
  width: 156px;
  color: #2e2113;
  text-shadow: none;
}

/* THE RIGHT STRIP IS THE PLAIN TAB. Wray, 2026-09-03: "the ribbons on the
   right of the leaderboard should be the regular tab-on / tab-off art."

   Same sheet, mirrored: the shaped cap is on the RIGHT, which is again the
   edge that sticks out past the page. Four files rather than one
   `scaleX(-1)`, for the same reason as before -- a CSS flip turns the label
   with it. */
body.skin-majesty .ws-tabs-r .wr-plank {
  border-image-source: url('/assets/ui/tab-off.png?v=2026-09-03a');
  border-image-slice: 0 4 0 1 fill;
  border-image-width: 0 10px 0 3px;
  border-width: 0 10px 0 3px;
  /* THE FIRST LETTER, AGAIN. The strip tucks 14px under the torn edge (see
     `.ws-tabs.ws-tabs-r` above), and what used to clear that tuck was the
     ribbon's own 15px left border. The tab art's left edge is square and only
     3px, so the parchment went straight back to eating the first letter --
     "oday", "eason", "ll time", exactly the symptom that note describes.
     13px of padding puts the label back at 16px from the tab's edge, two
     clear of the tuck. Measured on a 1440x900 window: sheet edge 1060, tab
     left 1046. */
  padding: 0 0 5px 13px;
  /* THE LABEL RIDES THE OUTER END (Wray, 2026-09-04): "text should be
     aligned to the right of the ribbon surface so it extends out, right now
     text sits the the left so the ribbons longer than it needs to be when
     highlighting." The tab grows rightward when chosen; a left-hugging label
     leaves the whole extension as empty ribbon. flex-end walks the words out
     with the tail; 13px right keeps them off the shaped cap. */
  justify-content: flex-end;
  text-align: right;
  padding-right: 13px;
  /* SIZED BY THE WORDS. At a fixed 104 the rest state clipped "All time" to
     "ll time" -- right-aligned text sheds its FIRST letters under the tuck,
     the exact regression the 13px note above describes, from the other end.
     Auto width also answers the actual complaint: a ribbon is as long as its
     label needs and no longer, so the chosen tab's run-out reads as growth
     rather than as slack being used up. */
  width: auto;
  min-width: 104px;
}
body.skin-majesty .ws-tabs-r .wr-plank[aria-current='page'] {
  border-image-source: url('/assets/ui/tab-on.png?v=2026-09-03a');
  /* the run-out, relative to its own words now the rest width is auto */
  width: auto;
  min-width: 156px;
}

@media (prefers-reduced-motion: reduce) {
  body.skin-majesty .ws-tabs .wr-plank { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════
   ⭐THE SYSTEM SURFACE — the game talking to you (#2557, 2026-09-08)
   ──────────────────────────────────────────────────────────────────────
   Albion runs TWO materials and we had only ever built one. Measured off the
   real client on 2026-09-08, both visible in a single frame:

     PARCHMENT is YOUR STUFF   — anything you browse, own, or pick from.
     A DARK SLAB is THE GAME   — explainers, the quest tracker, confirmations,
     TALKING TO YOU              notifications. Gold heading, square bullets.

   Wray's letter A ("every window is parchment", #2607) is CORRECT and is not
   reopened by this: all nine doors are your stuff. What this adds is the
   category we had no answer for, which is why help text and the quests panel
   have never sat right on paper.

   ⭐THE MATERIAL ALREADY EXISTS AND HE ALREADY LIKES IT. Wray, 2026-09-08:
   "I liked the darker majestic bitmap dark screens before." That is
   `panel-well.png`, which the journal and the notices have worn all along --
   the same dark navy well, the same cream ink. So this is not new art and not
   a new palette, it is the surface the world already speaks in, given a name
   and made reachable from a tab.

   ⛔NOT `screen.png`. That is the 108x53 WINDOW plate the Bag and the Arena
   wore before #2607, at a 12px slice. It is the material Wray's letter
   retired for windows, and reviving it here would read as reversing him. The
   well is the panel, and a system message is a panel.

   HOW A SURFACE IS CHOSEN: one word on the tab, `surface: 'system'`, read by
   `stampSurface` in world-shell.js. The default is the parchment and has no
   name, so nothing has to be written to get what everything already gets.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ws-sheet[data-surface='system'] {
  border-image-source: url('/assets/ui/panel-well.png?v=2026-08-29a');
  /* 4 art px at 32 is a clean 8x. The parchment's torn edge is 16 art px at
     32 (2x); this tile is 16x16 with a 4px rim, so the same 32px border keeps
     both windows the same THICKNESS while each stays a whole multiple of its
     own art -- which is the #2206 uneven-scale trap avoided in both. */
  border-image-slice: 4 fill;
  border-image-width: 32px;
  border-width: 32px;
  /* The inks are declared ON the window so everything inside inherits them.
     That is deliberate rather than incidental: a system window is dark, and a
     child that reads --sk-ink for its own colour must get the dark window's
     answer, not the body's. (The other half of that mechanism is what made
     eight doors report one size this morning -- a property resolves where it
     is declared, so declaring it here is exactly right and declaring it in the
     shared block would be exactly wrong.) */
  --sk-ink: #e8d5a9;
  --sk-ink-dim: #93a3b6;
  color: var(--sk-ink);
}

/* The title reads as the game's voice rather than a page's name. */
body.skin-majesty .ws-sheet[data-surface='system'] .ws-sheet-title { color: #e8d5a9; }

/* THE RIBBONS COME WITH IT. The strips are siblings of the sheet and inherit
   nothing from it, so without this the tabs stay parchment-inked beside a dark
   window. Same reason every per-door size is set on both. */
body.skin-majesty .ws-tabs[data-surface='system'] .wr-plank { color: #e8d5a9; }

/* A framed page inside a system window paints nothing of its own; the window's
   well is its ground, exactly as the parchment's paper is on the default. */
body.skin-majesty .ws-sheet[data-surface='system'] .ws-sheet-body iframe {
  background: transparent;
}

/* the sheet body names itself as a container so a fixed-size piece of
   content (the zone map) can pick a whole-multiple size from the room it is
   actually given -- see `.ww-mapsheet` */
body.skin-majesty .ws-sheet-body { container: ws-body / inline-size; }
body.skin-majesty .ws-sheet-body {
  border: 0;
  overflow: hidden;
  /* NO SECOND SHEET. The window itself is the parchment now; painting another
     one here is what made a door open a page inside a frame. */
  background: none;
  image-rendering: pixelated;
  /* GUTTERS ON BOTH SIDES, AND EQUAL. Wray, 2026-08-30: "the leaderboard
     [must] not clip against the ribbons on the left and be centred to the
     partchment frame."

     Both strips tuck back over the torn edge so they read as fixed INTO the
     page rather than floating beside it, which puts 14px of ribbon over the
     content at each edge. Equal padding does both jobs at once: it clears the
     ribbons, and it makes the content box symmetric, which is what lets
     `margin: 0 auto` actually centre. Unequal, the table sat 89px left of the
     parchment's middle while claiming to be centred. */
  padding: 0 22px 4px 22px;
}

/* THE IFRAME IS GLASS, NOT NAVY (#2557, 2026-09-06). This line painted
   every framed page's ground navy UNDER the page, so however framed-skin.css
   mapped the tokens -- and it makes the page transparent on purpose -- a
   navy slab showed through inside the parchment. Nine tabs contradicted the
   one-surface ruling from the inside for a week because of a rule in a file
   the token file could not see. Transparent: the window's paper is the
   page's ground now, the way it is for a native tab. */
body.skin-majesty .ws-sheet-body iframe { background: transparent; }

body.skin-majesty .ws-frame-head {
  font-family: var(--sk-tome);
  font-size: 32px;
  letter-spacing: 0;
  color: #3a2412;
}
body.skin-majesty .ws-frame-state { color: #2e2113; }


/* ══════════════════════════════════════════════════════════════════════
   NATIVE WINDOW CONTENT (world-windows.js)
   ──────────────────────────────────────────────────────────────────────
   Drawn on the sheet's parchment rather than framed, so this is ink on
   paper: dark type, Majesty's slot cells, no second frame. The window
   itself is already sized by --sk-win-w / --sk-win-h.
   ══════════════════════════════════════════════════════════════════════ */

body.skin-majesty .ws-sheet-body .ww-bag {
  /* a flex ITEM of .ws-sheet-body as well as a flex container: without the
     grow it was sized to its content and the bag grid collapsed to two
     columns in a window with room for eight */
  flex: 1 1 auto;
  width: 100%;
  display: flex;
  gap: 14px;
  height: 100%;
  min-height: 0;
  color: #2e2113;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 19px;
  line-height: 1.25;
}

body.skin-majesty .ww-h {
  font-family: var(--sk-tome);
  font-size: 16px;
  line-height: 1.2;
  margin: 0 0 6px;
  color: #3a2412;
  font-weight: normal;
}

body.skin-majesty .ww-sheet {
  flex: 0 0 168px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
}
body.skin-majesty .ww-sheet::-webkit-scrollbar { width: 0; }

body.skin-majesty .ww-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px 10px;
  margin-bottom: 10px;
}
body.skin-majesty .ww-stat { display: flex; justify-content: space-between; gap: 6px; }
body.skin-majesty .ww-k {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #6b5233;
  align-self: center;
}
body.skin-majesty .ww-v { font-variant-numeric: tabular-nums; }

body.skin-majesty .ww-doll { display: flex; flex-direction: column; gap: 3px; }
body.skin-majesty .ww-slot {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 6px;
  /* Majesty's own parchment cell, which is drawn for exactly this */
  background: url('/assets/ui/paper.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  box-shadow: inset 0 0 0 2px rgba(122, 90, 56, .45);
}
body.skin-majesty .ww-slot.is-empty { opacity: .55; }
body.skin-majesty .ww-slotk {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #6b5233;
  align-self: center;
}
body.skin-majesty .ww-slotv { text-align: right; }

body.skin-majesty .ww-items {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
body.skin-majesty .ww-bagtop {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
body.skin-majesty .ww-count { font-size: 17px; color: #6b5233; }

body.skin-majesty .ww-grid {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  /* auto-fill rather than a fixed count: the window is one size today and
     Wray will tune it, and a hardcoded column count would break the moment
     it changed */
  grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 4px;
  align-content: start;
  padding-right: 2px;
  scrollbar-width: thin;
  scrollbar-color: #8a7048 transparent;
}

body.skin-majesty .ww-cell {
  aspect-ratio: 1;
  border: 0;
  cursor: pointer;
  background: url('/assets/ui/paper.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  /* the rarity is the rim, because rarity is what a bag is scanned for */
  box-shadow: inset 0 0 0 3px var(--r, #9d8a6d);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  color: #3a2412;
  padding: 0;
}
body.skin-majesty .ww-cell:hover { filter: brightness(1.08); }
body.skin-majesty .ww-cell.is-worn { box-shadow: inset 0 0 0 3px var(--r), 0 0 0 2px #3a2412; }

body.skin-majesty .ww-none,
body.skin-majesty .ww-state {
  grid-column: 1 / -1;
  color: #6b5233;
  padding: 12px 4px;
}
body.skin-majesty .ws-sheet-body .ww-state {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 20px;
}


/* ── THE FOOT ──────────────────────────────────────────────────────────
   The strip below the tray, which exists because the picture is a whole
   multiple of 320x240 and the window is not: at 1440x780 that leaves ~60px
   under everything. Wray: "the very bottom extreme hosts something else".

   It carries the AMBIENT numbers — the ones you glance at rather than act
   on — which also takes the wallet chip off the rail and gives the doors
   back a row. Nothing here is a control except the wallet, which was
   already one.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty #wt-foot {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--sk-foot);
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 18px;
  /* ⛔6px OF BOTTOM PADDING, BECAUSE THE INPUT IS TALLER THAN THE STRIP.
     `#world-chat` is 30px in here but the field inside it renders 40 (the
     44px tap floor), so centred in a 38px row it measured y=794..834 against
     an 831 viewport -- the bottom 3px of the type box were off the screen.
     Padding lifts the row's content instead of growing --sk-foot, which is
     Wray's own saved number and is what the window band measures against. */
  padding: 0 16px 6px;
  /* ⭐NO BAR ANY MORE (Wray, 2026-09-07): "the bottom bar thats black. that
     needs removing and the 3d space bleeding into that, the chat box and cam
     buttons survive but on the 3d window now."
     The 3D canvas was ALREADY full-screen (measured 0,0,1920,1080) -- this
     strip was an opaque lid over the bottom 38px of it, not a region the
     render stopped short of. So the bleed is achieved by taking the ground
     and the border off, and nothing about the renderer changes.
     What stays is the ROW: it still lays the chat out left and the camera pad
     right, and --sk-foot still reserves the strip so a window's bottom edge
     clears them. The reservation is now "where the floating controls live"
     rather than "how tall the bar is". */
  background: transparent;
  border-top: 0;
  z-index: 55;
  /* was `hidden`, which CLIPPED the camera pad: the pad is 44px tall in a
     38px strip, so its bottom 6px were cut off before this. */
  overflow: visible;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 19px;
  color: var(--sk-ink-dim);
}
body.skin-majesty #wt-foot b {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  color: var(--sk-gold);
  font-weight: normal;
  letter-spacing: .06em;
}
body.skin-majesty #wt-foot .wf-v { color: var(--sk-ink); }

/* THE NUMBERS ARE BUTTONS NOW, so they have to look like something you can
   point at without turning the strip into a toolbar. A gold underline on
   hover and focus, and nothing at rest: the bar is ambient, and four plates
   along the bottom of the screen would compete with the doors. */
body.skin-majesty #wt-foot .wf-hit {
  background: none;
  border: 0;
  padding: 0 1px;
  font: inherit;
  color: var(--sk-gold, #caa982);
  cursor: pointer;
  border-bottom: 1px solid transparent;
}
body.skin-majesty #wt-foot .wf-hit:hover,
body.skin-majesty #wt-foot .wf-hit:focus-visible {
  border-bottom-color: var(--sk-gold, #caa982);
  outline: none;
}

/* ── who is out there ─────────────────────────────────────────────────
   Anchored above the foot bar rather than below the number, because the bar
   is already at the bottom of the screen and a list hanging off it would
   open off the edge. */
body.skin-majesty .wf-pop {
  position: fixed;
  z-index: 9000;
  min-width: 190px;
  max-width: 280px;
  max-height: 46vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 6px;
  box-sizing: border-box;
  border-style: solid;
  border-image-source: url('/assets/ui/screen.png?v=2026-08-29a');
  border-image-slice: 12 fill;
  border-image-width: 18px;
  border-width: 18px;
  image-rendering: pixelated;
  color: var(--sk-ink);
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
}
body.skin-majesty .wf-pop-h {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .08em;
  color: var(--sk-gold, #caa982);
  padding: 0 2px 5px;
}
body.skin-majesty .wf-pop-none {
  opacity: .55;
  padding: 2px;
}
body.skin-majesty .wf-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 4px 3px;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
body.skin-majesty .wf-row:hover,
body.skin-majesty .wf-row:focus-visible {
  background: rgba(202, 169, 130, .14);
  outline: none;
}
body.skin-majesty .wf-row-n { line-height: 1.1; }
body.skin-majesty .wf-row-s {
  font-size: 18px;
  /* opacity .6 on the dim ink was a third dilution; the ink is the dim */
  opacity: .85;
  line-height: 1.1;
}

/* ══════════════════════════════════════════════════════════════════════
   THE PROFILE CARD (#2461 step 01)
   ──────────────────────────────────────────────────────────────────────
   Pressing Profile on a cat used to do nothing at all. It now opens this,
   OVER the picture — a page change would reload the world and respawn you
   at the gate, and inspecting somebody has to cost less than a door.

   It STACKS ON THE TARGET PANE rather than floating anywhere: the pane is
   `right: 0; bottom: var(--sk-foot)` and 260px tall, so this sits exactly
   on top of it in the same rail-width column. The card is about the cat
   already shown below it, so the two read as one object.

   Same 9-slice screen and the same two bitmap faces as the roster popover
   above, because it is the same gesture in the same corner.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .wt-card {
  position: fixed;
  right: 0;
  bottom: calc(var(--sk-foot) + 260px);
  width: var(--fx, 320px);
  max-height: 44vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  box-sizing: border-box;
  padding: 4px;
  border-style: solid;
  border-image-source: url('/assets/ui/screen.png?v=2026-08-29a');
  border-image-slice: 12 fill;
  border-image-width: 18px;
  border-width: 18px;
  image-rendering: pixelated;
  color: var(--sk-ink);
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  z-index: 42;
}
body.skin-majesty .wt-card-h {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 2px 6px;
}
body.skin-majesty .wt-card-sig {
  width: 18px;
  height: 18px;
  flex: none;
  image-rendering: pixelated;
}
body.skin-majesty .wt-card-n {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .06em;
  color: var(--sk-gold, #caa982);
}
body.skin-majesty .wt-card-x {
  flex: none;
  background: none;
  border: 0;
  padding: 2px 4px;
  cursor: pointer;
  color: var(--sk-ink-dim, #8a94a3);
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
}
body.skin-majesty .wt-card-x:hover,
body.skin-majesty .wt-card-x:focus-visible {
  color: var(--sk-gold, #caa982);
  outline: none;
}
body.skin-majesty .wt-card-r {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 2px 3px;
  line-height: 1.15;
}
body.skin-majesty .wt-card-r b {
  font-weight: normal;
  opacity: .55;
}
/* the numbers line up in their own column */
body.skin-majesty .wt-card-r i {
  font-style: normal;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
body.skin-majesty .wt-card-wait {
  opacity: .55;
  padding: 3px;
}
body.skin-majesty .wt-card-more {
  display: block;
  margin-top: 6px;
  padding: 3px;
  color: var(--sk-gold, #caa982);
  text-decoration: none;
  border-top: 1px solid rgba(202, 169, 130, .22);
}
body.skin-majesty .wt-card-more:hover,
body.skin-majesty .wt-card-more:focus-visible {
  background: rgba(202, 169, 130, .14);
  outline: none;
}


/* ══════════════════════════════════════════════════════════════════════
   THE FRAME ROUND THE PICTURE (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray asked for this twice — "perhaps a small frame around the game window
   itself?" and then "still no frame on the 3d window" — and both times I
   fixed the frame on the POP-UP instead, which is a different element and
   was working. This is the one he meant.

   It is a sibling of the render rather than a border on it, because the
   engine owns that canvas and its size is the whole-multiple rule. Drawn
   over the picture's edge and NOT clickable: a frame that eats taps on the
   tiles under it would break walking, which is the one thing the picture is
   for.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty #wt-frame {
  /* THE FRAME OVERLAPS THE PICTURE'S EDGE, and it has to. The render fills
     the gap between the rails exactly, so a frame drawn OUTSIDE it lands
     under the rails and is invisible (tried), and one drawn at 48px eats 48px
     of scene on every side (also tried). 24px is the art at x2 — a whole
     multiple, so still crisp — and it costs the outermost 24px of a 640x480
     picture, which is sky and letterbox. */
  position: fixed;
  left: var(--fx, 0px);
  top: var(--fy, 0px);
  width: var(--fw, 100vw);
  height: var(--fh, 100vh);
  border-style: solid;
  border-image-source: url('/assets/ui/screen.png?v=2026-08-29a');
  /* TWO UPRIGHTS, NOT A BOX. Wray: "it needs to be much thinner, and it
     shouldn't enter the three d window at all. It basically wants to sit each
     side left and right, and it doesn't matter too much if we bleed onto the
     canvas by one or two pixels on the top and the bottom."

     So the top and bottom borders are ZERO and only the side edges draw. At
     12px — the art at x1 — each upright costs twelve pixels of sky rather
     than the twenty-four a x2 box was taking off every side. The horizontal
     slices stay 12 so the art's own edge pattern is what repeats down them. */
  /* THE THICKNESS IS A KNOB. Wray, 2026-08-30: "i can edit by moving but i
     cant make it any wider so there is no way to fit it properly."

     He was right and the editor could not have helped: the frame's WIDTH is
     the render rectangle, so widening the element does nothing useful -- what
     wants to change is how thick the two uprights are, which is a
     border-width. It is `--sk-frame-w` now, and a slider drives it. */
  border-image-slice: 12;          /* no fill: the picture is the middle */
  border-image-width: 0 var(--sk-frame-w, 12px);
  border-width: 0 var(--sk-frame-w, 12px);
  border-image-repeat: repeat;
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 37;                     /* under the rails (38), over the canvas */
}
/* ⭐NO UPRIGHTS UNDER FULL BLEED (Wray, 2026-09-12: "take off thin left and
   right frame from the 3d window thats now full bleed").

   The two uprights were drawn to mark where the PICTURE ended and the
   letterbox gutter began -- that is the whole of what they said. Under
   full bleed the picture is the screen, so there is no edge to mark, and
   the twelve columns each side were a dark stripe down the middle of the
   scene's sky and water for no reason. Measured on the served world at
   1920x1080 before this: x 0-11 and x 1908-1917 were #182029 top to bottom,
   inside a canvas the engine had already filled edge to edge.

   The letterbox path keeps them: a phone still bleeds through the phone
   branch of `frame-fill-desktop` (which is never set there), and a desktop
   with the full-bleed flag off still letterboxes and still wants the frame
   Wray asked for twice in 2026-08. The class is the same signal the rail
   reads (world-frame-fill.js). */
body.skin-majesty.frame-fill-desktop #wt-frame { display: none; }


/* ── the inventory window's own tabs and pages ────────────────────────
   Its ribbons are the same ribbons as everywhere else: red closed, gold open,
   the length carrying the state. A window with more than one page uses them,
   which is what Wray asked for — "if one of the windows should have multiple
   tabs, we can use the ribbons". */

/* THE WINDOW'S TABS AND THE PAGE'S TABS ARE TWO DIFFERENT THINGS, and they
   were landing on each other: the door's ribbons hang top-left, and these
   started there too. A window can carry a door's pages AND its own, so the
   page's ribbons sit to the RIGHT of the door's and the content clears both.
   If a window ever needs more of either than fit on one line, that is the
   signal it is really two doors. */
body.skin-majesty .ww-marks {
  display: flex;
  gap: 8px;
  padding-left: 126px;
  margin: -10px 0 -28px;
  height: 34px;
  position: relative;
  z-index: 2;
  flex: 0 0 auto;
}
body.skin-majesty .ww-mark {
  width: 30px;
  height: 62px;
  border-style: solid;
  border-image-source: url('/assets/ui/ribbon-closed.png?v=2026-08-29b');
  border-image-slice: 5 0 4 0 fill;
  border-image-width: 10px 0 8px 0;
  border-width: 10px 0 8px 0;
  border-image-repeat: repeat;
  background: none;
  box-shadow: none;
  border-radius: 0;
  image-rendering: pixelated;
  cursor: pointer;
  padding: 0 0 10px;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: height 140ms steps(4, end);
}
body.skin-majesty .ww-mark:hover { height: 74px; }
body.skin-majesty .ww-mark:not(.off) {
  border-image-source: url('/assets/ui/ribbon-open.png?v=2026-08-29b');
  height: 86px;
}
body.skin-majesty .ww-mark i {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  font-style: normal;
  color: #f6ecd6;
  writing-mode: vertical-rl;
  letter-spacing: .06em;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
}
body.skin-majesty .ww-mark:not(.off) i { color: #2e2113; text-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  body.skin-majesty .ww-mark { transition: none; }
}

body.skin-majesty .ws-sheet-body .ww-bag {
  flex-direction: column;
  gap: 0;
}
body.skin-majesty .ww-page {
  flex: 1 1 auto;
  min-height: 0;
  /* clears the OPEN ribbon, which is 86px hanging from -10: 76 into the page.
     40 left the gold one sitting across the stats. */
  padding-top: 68px;
  display: flex;
  overflow: hidden;
}
body.skin-majesty .ww-cols {
  display: flex;
  gap: 14px;
  width: 100%;
  min-height: 0;
}
body.skin-majesty .ww-doll {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3px;
  align-content: start;
  overflow-y: auto;
  scrollbar-width: none;
}
body.skin-majesty .ww-doll::-webkit-scrollbar { width: 0; }


/* the empty wallet box would still take the pad's flex gap */
body.skin-majesty .ws-rail.wrt-on .ws-wallet:empty { display: none; }

/* THE STRIP OVERHANGS THE RAIL'S RIGHT PADDING, so a ribbon at 100% ends
   exactly where the picture begins. The rail keeps its 12px for everything
   else -- the hero card wants that breathing room and the ribbons do not,
   because a ribbon that stops 12px short of the letterbox reads as
   misaligned rather than as inset. Wray: "the left pane ribbons dont touch
   the 3d window aligned like in localhost."

   The rest width is now exactly 100%, so a shut ribbon lands ON that line and
   an open one carries `--sk-tab-over` past it, onto the frame's upright. The
   extension used to be 30px against a fixed 270px ribbon, then 30px against
   whatever the rail is; it is now 8px measured against the FRAME, which is
   the thing it is meant to relate to. Verified on a 1440x900 window: --fx
   240, shut ribbons end at 240, the open one at 248, and the upright's last
   4 columns still show. */
body.skin-majesty .wrt-strip {
  margin-right: -12px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;   /* they grow to the RIGHT, so left edges align */
  gap: 6px;
  padding: 6px 0 0;
}

/* ── WHY EVERY RIBBON LABEL SAT LOW ───────────────────────────────────────
   Wray, 2026-09-03: "text that is on the ribbons is always too low and hugs
   the bottom of the ribbon so nudge text up a few px."

   It was not a taste problem and the number is not a guess. The art was
   measured row by row (39x15, all four cuts identical in profile):

     row  0      dark top edge
     rows 1-8    THE FACE — the lit band a label is meant to sit on
     row  9      dark bottom edge
     row  10     the shadow lip under the ribbon
     row  11     dark
     rows 12-14  the swallowtail tail, 2-3 opaque px wide, only at the caps

   So the readable face occupies art y 1→9 and its centre is y 5.0, while the
   ELEMENT's centre is y 7.5. `align-items: center` centres the label on the
   whole box, six of whose fifteen rows carry no face at all — so every label
   in the skin sat 2.5 art rows below the band it belongs to, which at these
   sizes is a visible slump onto the shadow lip.

   The correction is that offset scaled to the element:

     nudge = height x (7.5 - 5.0) / 15 = height / 6

   Applied as `padding-bottom: height/3`, because with `align-items: center`
   a bottom padding moves the content up by HALF of it — which keeps one rule
   doing the centring instead of adding a transform that later has to be
   remembered when the height changes. The icon travels with the label, since
   it sits on the same band and was equally low.
   ───────────────────────────────────────────────────────────────────────── */
body.skin-majesty .ws-rail.wrt-on .wrt-tab {
  /* At rest they end exactly on the picture's left edge. The gold one runs
     out past its neighbours as it always did -- the difference is that it now
     runs onto the frame rather than merely catching up to it. */
  width: var(--sk-tab-w, 100%);
  min-width: 0;
  height: 36px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  /* ON THE FACE, NOT ON THE SHADOW LIP. 36/3 = 12, which lifts the row by 6
     onto the band's centre. See "WHY EVERY RIBBON LABEL SAT LOW" above. */
  padding: 0 0 12px;
  border-style: solid;
  /* THE TURNED PAGE MARKER, NOT THE SIDE TAB. Wray, 2026-08-29: "its the
     correct ribbon-open ribbon-closed but rotated 90 degrees. this rule is
     for all the ribbons on the left rail, its the horizontal one rotated not
     the tab art."

     ribbon-closed-h is markR laid on its side: the decorated head becomes the
     LEFT cap, anchored to the rail, and the swallowtail becomes the RIGHT
     one, pointing out into the room. Rotated as PIXELS in export-skin.py,
     because a border-image cannot be rotated by CSS at all and rotating the
     element would turn the label with it.

     39x15 art. Slices 5 left / 4 right are the head and the notch; nothing
     top or bottom, so the 15px height stretches to the element. */
  border-image-source: url('/assets/ui/ribbon-closed-h.png?v=2026-08-29d');
  border-image-slice: 0 4 0 5 fill;
  border-image-width: 0 12px 0 15px;
  border-width: 0 12px 0 15px;
  border-image-repeat: stretch;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  color: #f0e4cd;
  cursor: pointer;
  transition: width 120ms steps(5, end);
}

/* THE EXTENSION IS STILL ABSOLUTE, on purpose: it is a gesture, and a
   gesture that scaled with the rail would be a big lurch on a wide one and
   invisible on a narrow one.

   THE `min(100%, ...)` CLAMP IS GONE from both this and the open state. It
   existed to stop a ribbon running out over the picture, which is now the
   whole point -- left in, it would pin every state to exactly 100% and
   nothing would move at all. Hover goes half the distance, so hovering reads
   as a preview of opening rather than as the same event. */
body.skin-majesty .ws-rail.wrt-on .wrt-tab:hover {
  width: calc(var(--sk-tab-w, 100%) + (var(--sk-tab-over, 8px) / 2));
}

/* GOLD IS THE OPEN DOOR, and the shell's own aria-current is what says so.
   No second piece of state, so a door opened by any route lights its
   ribbon. */
body.skin-majesty .ws-rail.wrt-on .wrt-tab[aria-current='page'] {
  border-image-source: url('/assets/ui/ribbon-open-h.png?v=2026-08-29d');
  width: calc(var(--sk-tab-w, 100%) + var(--sk-tab-over, 8px));
  color: #2e2113;                 /* ink on gold, not cream */
}

body.skin-majesty .ws-rail.wrt-on .wrt-tab .ws-ico {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin: 0;
}

body.skin-majesty .ws-rail.wrt-on .wrt-tab b {
  /* THE LABEL IS BACK. It came off when the ribbons were 34px-wide vertical
     banners with no room for it; a horizontal ribbon is nearly all room, and
     an icon-only nav makes people learn seven glyphs to find the Bag. */
  display: block;
  font-size: 15px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  text-shadow: none;
}
/* NOT a blanket `i`. world-shell.js builds the door glyph as
   `el('i', 'ws-ico')`, so hiding every <i> in a tab hid every icon in the
   rail — five ribbons with labels and no glyphs, which looked like the sprite
   sheet had failed to load. Only the hint line goes. */
body.skin-majesty .ws-rail.wrt-on .wrt-tab .ws-hint { display: none; }

body.skin-majesty .ws-rail[data-edge='left'] .ws-skill * {
  text-decoration: none;
  color: var(--sk-ink-dim);
}

/* ── THE CHAT PANEL IN THE LEFT RAIL (Wray, 2026-09-04) ───────────────────
   "chat needs moving to the left rail, then we can maybe fit in tabs for
   guild chats, local, party etc." The panel: channel chips, the log (chat's
   first HISTORY), the same input form. Pushed to the rail's foot with
   margin-top:auto so the doors keep the top. Only Local renders until the
   other channels exist server-side -- no dead tabs. */
body.skin-majesty .wp-chatpanel {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 150px;
}
body.skin-majesty .wp-chans { display: flex; gap: 6px; }
body.skin-majesty .wp-chan {
  border-style: solid;
  border-image-source: url('/assets/ui/tab-off.png?v=2026-09-03a');
  border-image-slice: 0 4 0 1 fill;
  border-image-width: 0 8px 0 2px;
  border-width: 0 8px 0 2px;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  height: 22px;
  padding: 0 6px 2px 6px;
  cursor: pointer;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  line-height: 1;
  color: #f6ecd6;
}
body.skin-majesty .wp-chan.is-on {
  border-image-source: url('/assets/ui/tab-on.png?v=2026-09-03a');
  color: #2e2113;
}
body.skin-majesty .wp-log {
  flex: 1 1 auto;
  min-height: 60px;
  /* the lab's `chat history` knob drives this (Wray asked for the handle) */
  max-height: var(--sk-chatlog-h, 180px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgba(0, 0, 0, .28);
  padding: 6px 8px;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 16px;
  line-height: 1.25;
  color: var(--sk-ink);
}
body.skin-majesty .wp-log::-webkit-scrollbar { width: 0; }
body.skin-majesty .wp-line { margin: 0 0 3px; word-break: break-word; }
body.skin-majesty .wp-line b { color: var(--sk-gold-hi); font-weight: 400; }
body.skin-majesty .wp-line.is-mine b { color: var(--sk-hot); }
/* SAID BEFORE YOU GOT HERE (#2461 step 03). Dimmed rather than labelled: a
   "— earlier —" divider is a second thing to read, and the only question a
   replayed line has to answer is "is somebody talking to me right now". The
   server already caps how old these can be, so nothing stale gets this far. */
body.skin-majesty .wp-line.is-old { opacity: .58; }
/* AN EMOTE IS A THING YOU DID (#2461 step 05). Italic and the room's own
   colour rather than the speaker gold, so "stormcat waves" reads as an action
   at a glance and never as somebody saying the word "waves". */
body.skin-majesty .wp-line.is-emote { font-style: italic; }
body.skin-majesty .wp-line.is-emote b { color: var(--sk-ink-dim, #8a94a3); }
/* A LEGENDARY IS LOUD (Wray's call, 2026-09-05). Only the top tier changes
   register, so the room can tell somebody opening a pack from somebody
   pulling the thing everyone wants. The COPY stays lowercase — house style —
   and the shouting happens here, which also means this can become bigger or
   gold or animated later without touching a string. */
body.skin-majesty .wp-line.is-loud {
  text-transform: uppercase;
  font-style: normal;
  letter-spacing: .04em;
  opacity: 1;
}
body.skin-majesty .wp-line.is-loud,
body.skin-majesty .wp-line.is-loud b { color: var(--sk-gold-hi, #e6c07a); }

/* the form stops being a fixed strip once it lives in the panel. Every
   property the fixed placement set has to be answered, or half of it
   lingers (the lesson at #world-chat's own comment above). */
body.skin-majesty #world-chat.wp-in-rail {
  position: static;
  left: auto; right: auto; top: auto; bottom: auto;
  width: 100%;
  min-width: 0;
  transform: none;
}
body.skin-majesty #world-chat.wp-in-rail .wp-verb { display: none; }

/* ── THE GATHERING TRACKS (#2641) ──────────────────────────────────────
   Was three centred chips reading "Mining 3". The endpoint has always sent
   the progress within the level and what the next one costs, and the window
   drew neither — a number that only goes up is a score, not a track.

   A ROW, NOT A CHIP, and the same row the quest log uses: a name, a value on
   the right, and a quiet line under it. Three of them stack rather than sit
   in a line, because a bar needs width and a chip has none. */
body.skin-majesty .wc-skills {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0 2px;
}
body.skin-majesty .wc-track { display: flex; flex-direction: column; gap: 3px; }
body.skin-majesty .wc-track-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
body.skin-majesty .wc-track-head b {
  font-family: var(--sk-head);
  font-size: 8px;
  letter-spacing: .5px;
  font-weight: 400;
  color: var(--sk-ink);
}
body.skin-majesty .wc-track-lv {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 17px;
  color: var(--sk-ink);
}
/* the bar is a well with a fill, both flat: a 9-slice at 6px tall is all
   border and no middle (the #2206 lesson, one control down) */
body.skin-majesty .wc-track-bar {
  height: 6px;
  background: rgba(0, 0, 0, .28);
  box-shadow: inset 0 0 0 1px rgba(122, 90, 56, .35);
  overflow: hidden;
}
body.skin-majesty .wc-track-fill {
  display: block;
  height: 100%;
  background: #8a6420;
  /* no transition: the window re-renders wholesale, so a tween would animate
     from zero on every repaint and read as the bar resetting */
}
body.skin-majesty .wc-track.is-max .wc-track-fill { background: #6a8f5a; }
body.skin-majesty .wc-track-note {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  color: var(--sk-ink-dim);
}

/* inside the parchment window the cream inks are invisible — the paper inks
   are literals here, as everywhere in a sheet */
body.skin-majesty .wc-window .wc-track-head b,
body.skin-majesty .wc-window .wc-track-lv { color: #2e2113; }
body.skin-majesty .wc-window .wc-track-note { color: #5a4328; }
body.skin-majesty .wc-window .wc-track-bar {
  background: rgba(46, 33, 19, .16);
  box-shadow: inset 0 0 0 1px rgba(46, 33, 19, .28);
}

/* ── THE ROOMS NAVIGATOR (Wray, 2026-09-04: "similar to how habbos
   navigation was good"): rows on the page, each carrying its count. The
   count column is gold because it is the information; the row you are in
   gets the marker instead of a different colour alone. */
body.skin-majesty .ws-nav-h {
  font-family: var(--sk-tome);
  font-size: 16px;
  /* ⛔NOT THE GOLD. --sk-gold-hi (#caa982) is a rail colour and the Rooms
     list sits on parchment, where it measured 1.1:1: "The Castle" had been
     invisible since the list moved onto the sheet (found 2026-09-06, fixed
     2026-09-22, #2557). The tome takes the ink every other heading on the
     paper takes. */
  color: #3a2412;
  padding: 2px 2px 6px;
}
/* YOUR BALANCE, at the head of the $9LV tab (2026-09-07). The label is the
   pixel head and the value is the body face, so a number that may be a
   sentence ("no wallet connected") sits on the same line without the layout
   caring which it is. */
body.skin-majesty .ww-bal {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 0 2px 8px;
  border-bottom: 2px solid rgba(122, 90, 56, .35);
  margin-bottom: 10px;
}
body.skin-majesty .ww-bal b {
  font-family: var(--sk-head);
  font-size: 9px;
  letter-spacing: 1px;
  color: #8a6420;
}
body.skin-majesty .ww-bal-v {
  font-family: var(--sk-body);
  font-size: 20px;
  color: #2e2113;
  text-align: right;
}

/* ── THE QUEST LOG (#2557, 2026-09-07) ────────────────────────────────
   Wray ruled the log earns a door and the corner panel becomes its tracker.
   The row here is deliberately the SAME OBJECT as the mode list and the room
   row below: a mark, a name in the tome, a quiet description under it, and
   the value on the right. A quest is a thing you scan down a list, so it gets
   the list idiom rather than a shape of its own.

   ⛔THE INKS ARE LITERALS, not tokens. `--sk-ink` is #f0e2c6 cream, correct
   for the navy the skin began as and invisible on the parchment these windows
   are now. Inside a sheet: #2e2113 ink, #5a4328 muted, #8a6420 gold. */
body.skin-majesty .ww-quests { padding: 2px 0 8px; }

body.skin-majesty .ww-q-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 0 2px 6px;
  margin: 14px 0 8px;
  border-bottom: 2px solid rgba(122, 90, 56, .35);
}
body.skin-majesty .ww-quests > .ww-q-head:first-child { margin-top: 0; }
body.skin-majesty .ww-q-head b {
  font-family: var(--sk-head);
  font-size: 9px;
  letter-spacing: 1px;
  color: #8a6420;
}
body.skin-majesty .ww-q-count {
  font-family: var(--sk-body);
  font-size: 17px;
  color: #5a4328;
}

body.skin-majesty .ww-q-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 4px;
  border-bottom: 1px solid rgba(122, 90, 56, .18);
}
body.skin-majesty .ww-q-row:last-child { border-bottom: 0; }

/* THE MARK IS A GLYPH, NOT A CONTROL. Nothing in this list is pressable
   except CLAIM, so a box that looks like a checkbox would be a lie. */
body.skin-majesty .ww-q-mark {
  flex: 0 0 auto;
  width: 16px;
  font-family: var(--sk-head);
  font-size: 12px;
  line-height: 1.4;
  color: #8a6420;
  text-align: center;
}
body.skin-majesty .ww-q-txt { flex: 1 1 auto; min-width: 0; }
body.skin-majesty .ww-q-name {
  font-family: var(--sk-head);
  font-size: 9px;
  letter-spacing: .5px;
  color: #2e2113;
  margin-bottom: 3px;
}
body.skin-majesty .ww-q-desc {
  font-family: var(--sk-body);
  font-size: 17px;
  line-height: 1.15;
  color: #5a4328;
}
body.skin-majesty .ww-q-pts {
  flex: 0 0 auto;
  font-family: var(--sk-body);
  font-size: 17px;
  color: #8a6420;
  align-self: center;
}
/* A DONE QUEST STAYS AND GOES QUIET. It is not removed: the tracker drops the
   day's panel once all three are done, and a log you cannot open to see what
   you finished is not a log. That difference is the whole point of having
   both surfaces. */
body.skin-majesty .ww-q-row.is-done .ww-q-name,
body.skin-majesty .ww-q-row.is-done .ww-q-desc { color: #9d8a6d; }
body.skin-majesty .ww-q-row.is-done .ww-q-mark { color: #6a8f5a; }

body.skin-majesty .ww-q-claim {
  flex: 0 0 auto;
  align-self: center;
  min-height: 44px;              /* #2535's tap floor, not negotiable */
  padding: 0 14px;
  font-family: var(--sk-head);
  font-size: 8px;
  letter-spacing: 1px;
  color: var(--sk-ink);
  cursor: pointer;
  /* ⛔THE SLICE HAS TO FIT THE PICTURE (#3031 2K, 2026-09-24). This read
     `16 24 16 24` on btn-on.png, which is 16px square: top and bottom slices
     that meet in the middle and sides wider than the image leave the nine-
     slice with no middle and no edges, and CLAIM drew as a few loose pixels
     round its word on the parchment (measured on the served window with a
     tier ready). Nobody had seen it because a tier is rarely ready when the
     window is opened. The plank is now the one every other button here
     wears: btn-idle at 4 5 5 5, at the skin's 2x, lit on hover. */
  border-style: solid;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: 8px 10px 10px 10px;
  border-width: 8px 10px 10px 10px;
  border-image-repeat: repeat;
  background: none;
  image-rendering: pixelated;
}
body.skin-majesty .ww-q-claim:hover:not(:disabled) { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ww-q-claim:disabled { opacity: .5; cursor: default; }
body.skin-majesty .ww-q-empty {
  font-family: var(--sk-body);
  font-size: 17px;
  color: #5a4328;
  padding: 6px 4px;
}

/* ── THE QUEST LOG YOU CAN SCAN (#2757, 2026-09-12) ───────────────────
   Wray: "its readable but theres something missing still, its not eye
   catching and a little boring to read." Three drawn things, no new art:
   a bar under each header, a seal and a chip on every row, and the week as
   a track with notches instead of four rows. Every material here already
   exists elsewhere in this file: the CARRY trough, the chat verbs' plate,
   the tome face the Play rows use. */

/* the bars: the CARRY trough, gold, green when complete */
body.skin-majesty .ww-q-meter { padding: 0 2px 8px; }
body.skin-majesty .ww-q-meter .ws-meter {
  display: block;
  height: 10px;
  border: 2px solid #33240f;
  background: #3c2c18;
  border-radius: 0;
  overflow: hidden;
}
body.skin-majesty .ww-q-meter .ws-meter::after { content: none; }
body.skin-majesty .ww-q-meter .ws-meter > * {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  height: auto;
  max-width: none;
  background: repeating-linear-gradient(90deg, #c49a3e 0 12px, #8a6d2f 12px 16px);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .22), inset 0 -2px 0 rgba(0, 0, 0, .3);
}
body.skin-majesty .ww-q-meter.is-full .ws-meter > * {
  background: repeating-linear-gradient(90deg, #6a8f5a 0 12px, #4c6b40 12px 16px);
}

/* the row: seal | name over description | chip. The seal is a 22px square
   ring; done, it fills gold with the cross in ink. The name takes the tome
   at reading size (it was 9px pixel caps) so a row has a head. */
body.skin-majesty .ww-q-row { align-items: center; gap: 12px; padding: 9px 4px; }
body.skin-majesty .ww-q-row .ww-q-mark {
  width: 22px;
  height: 22px;
  box-sizing: border-box;
  border: 2px solid rgba(122, 90, 56, .55);
  background: rgba(0, 0, 0, .06);
  font-size: 13px;
  line-height: 18px;
  color: #2e2113;
}
body.skin-majesty .ww-q-row.is-done .ww-q-mark {
  background: #c49a3e;
  border-color: #8a6420;
  color: #2e2113;
}
/* THE SEAL'S GLYPH (#2757 slice 2). quest-icons.png is a colour row over a
   grey row, 16px cells, one column per quest id (--qx, set by the renderer
   from QUEST_GLYPH). The 22px seal is border-box with a 2px ring, so the
   padding box is 18px and the glyph sits at 1px in; the background is drawn
   inside the ring, never over it. Open: the grey row in the empty ring.
   Done: the colour row on the gold fill, and the cross steps aside for it
   (font-size 0 rather than removing the text, so the renderer's mark stays
   the same element with the same content and a row with no column keeps
   its cross exactly as before). */
body.skin-majesty .ww-q-row .ww-q-mark[data-q] {
  font-size: 0;
  background-image: url('/assets/ui/quest-icons.png?v=2026-09-22a');
  background-size: 128px 32px;
  background-repeat: no-repeat;
  background-position: calc(var(--qx, 0) * -16px + 1px) -15px;
  image-rendering: pixelated;
}
body.skin-majesty .ww-q-row.is-done .ww-q-mark[data-q] {
  background-color: #c49a3e;
  background-position: calc(var(--qx, 0) * -16px + 1px) 1px;
}
body.skin-majesty .ww-q-row .ww-q-name {
  font-family: var(--sk-tome);
  font-size: 18px;
  letter-spacing: 0;
  margin-bottom: 1px;
}
body.skin-majesty .ww-q-row.is-done .ww-q-name { text-decoration: line-through; text-decoration-thickness: 2px; }
/* ⛔THE CHIP IS A LABEL, NOT A PLANK (#3031 2K, 2026-09-24). It wore the
   chat verbs' button plate (btn-idle, bright once paid), and the plan's audit
   of the served window said it plainly: "reward chips look like buttons".
   Nothing in a quest row is pressable, so the reward reads as a figure in
   the ledger's gold with its unit, and turns green once it has paid. */
body.skin-majesty .ww-q-chip {
  font-family: var(--sk-body);
  font-size: 18px;
  line-height: 1;
  letter-spacing: 0;
  color: #8a6420;
  text-align: right;
  white-space: nowrap;
}
body.skin-majesty .ww-q-row.is-done .ww-q-chip { color: #4c6b40; }
/* A PAYMENT THAT PAYS BOTH NUMBERS SHOWS BOTH (#3151, NAMES 1A): "+30
   Infamy" over "+30 Obols", stacked at the row's right edge so a phone row
   keeps its description's width. */
body.skin-majesty .ww-q-pay {
  flex: 0 0 auto;
  align-self: center;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
/* each line to the right edge: `.ww-q-pts` centres itself (align-self,
   above), which beat the stack's flex-end and centred "+35 Obols" under
   "+35 Infamy" (measured 1774-1838 under 1770-1842, review of PR #3303) */
body.skin-majesty .ww-q-pay > .ww-q-pts { align-self: flex-end; }

/* the week as a track: the bar, then the notches stood on it by their share
   of the way. Each notch is the old tier row turned into a column: the seal
   (its count, or the cross once claimed), the reward under it, CLAIM under
   that when it is ready. Absolute by percentage, translated back by half so
   the seal sits ON the notch; the last one is pinned to the end so its
   column does not hang off the parchment. */
/* ⛔EQUAL COLUMNS (#3031 2K, 2026-09-24). The notches stood at each tier's
   share of the way (30/50/70/100%) as 76px columns with the last pinned to
   the end, and on a phone's 250px track 7's rewards ran into 10's ("+1
   pack2 packs", measured). Now the notches are a grid of --n equal columns,
   each seal at its column's middle, and the renderer's weekFill() fills the
   bar between them. The track is padded evenly so the bar and the columns
   span the same width. */
body.skin-majesty .ww-q-track { position: relative; padding: 0 2px; margin-top: 2px; }
body.skin-majesty .ww-q-track .ww-q-meter { padding: 0 0 0; }
body.skin-majesty .ww-q-notches {
  display: grid;
  grid-template-columns: repeat(var(--n, 4), minmax(0, 1fr));
  margin-top: 6px;
}
body.skin-majesty .ww-q-notch {
  /* positioned, so the seal paints OVER the bar it stands on: the bar's
     trough is positioned, and a static column paints under it (measured:
     the top half of every seal was hidden behind the trough) */
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 0;
  text-align: center;
}
body.skin-majesty .ww-q-notch .ww-q-nmark {
  width: 26px;
  height: 26px;
  box-sizing: border-box;
  margin-top: -21px;                 /* stands on the bar */
  border: 2px solid #33240f;
  background: #3c2c18;
  font-family: var(--sk-head);
  font-size: 9px;
  line-height: 22px;
  color: #d5b68c;
  text-align: center;
}
body.skin-majesty .ww-q-notch.is-done .ww-q-nmark { background: #c49a3e; border-color: #8a6420; color: #2e2113; }
body.skin-majesty .ww-q-notch.is-claimed .ww-q-nmark { background: #6a8f5a; border-color: #4c6b40; color: #f0e2c6; font-size: 13px; }
body.skin-majesty .ww-q-notch .ww-q-txt { flex: 0 0 auto; }
body.skin-majesty .ww-q-notch .ww-q-nname { display: none; }   /* the seal carries the number */
body.skin-majesty .ww-q-notch .ww-q-ndesc { font-size: 14px; line-height: 1; color: #8a7355; }
body.skin-majesty .ww-q-notch .ww-q-rew {
  display: flex;
  flex-direction: column;
  align-items: inherit;
  gap: 1px;
  font-family: var(--sk-body);
  font-size: 16px;
  line-height: 1.05;
  color: #8a6420;
}
/* ⛔ONE LINE EACH, AND ROOM BETWEEN THE TIERS (#3151, review of PR #3303).
   "+300 Infamy" at 16px is 70px, so on the 1366 dock's 74px columns the
   tiers stood 4px apart and "+100 Infamy +200 Infamy +300 Infamy" read as
   one run, and on a 320px phone's 62px columns it broke into "+300 /
   Infamy", so the four columns grew to different heights and CLAIM lost
   its level (#3031 2K). At 14px, and never wrapped, it is 61px; the 359px
   rule below takes it to 12px for the narrowest phones. */
body.skin-majesty .ww-q-notch .ww-q-rew .ww-q-pts { font-size: 14px; white-space: nowrap; }
/* three lines held for every tier's reward (Infamy, Obols and a pack since
   #3151 names both halves of the pay), so a CLAIM under a tier that pays a
   pack sits level with one under a tier that does not (#3031 2K) */
body.skin-majesty .ww-q-notch .ww-q-rew { min-height: 3.2em; }
/* the pack line in the body face, not the pixel caps: at 8px caps "+2 packs"
   under the last notch ran into "+1 pack" under the one before it (measured
   on the 461px dock); VT323 at 15 is half the width and still reads */
body.skin-majesty .ww-q-notch .ww-q-pack {
  font-family: var(--sk-body);
  font-size: 15px;
  letter-spacing: 0;
  color: #2e2113;
}
/* narrow enough for a phone's 68px column: no padding beside the word, so
   CLAIM is 60px wide; the 44px tap floor stays */
body.skin-majesty .ww-q-notch .ww-q-claim {
  padding: 0;
  max-width: 100%;
  margin-top: 2px;
}
/* a 320px phone gives each column about 53px: the plank's side caps come in
   to 6px so the word stays on it (52px) */
@media (max-width: 359px) {
  body.skin-majesty .ww-q-notch .ww-q-claim {
    border-width: 8px 6px 10px 6px;
    border-image-width: 8px 6px 10px 6px;
  }
  /* "+300 Infamy" on one line inside the narrowest column (#3151) */
  body.skin-majesty .ww-q-notch .ww-q-rew .ww-q-pts { font-size: 12px; }
}
/* the countdowns: when the day's three and the week's track turn over */
body.skin-majesty .ww-q-reset {
  font-family: var(--sk-body);
  font-size: 16px;
  color: #8a7355;
  padding: 6px 4px 0;
  margin: 0;
}
/* earlier this week: the same row, a size quieter, the day where the
   description goes */
body.skin-majesty .ww-q-row.ww-q-past { padding: 5px 4px; }
/* a log entry is finished, not struck out, and it is there to be read, so
   it keeps the ink the day's open quests use rather than the faded done ink */
body.skin-majesty .ww-q-row.ww-q-past .ww-q-name { font-size: 16px; text-decoration: none; color: #5a4328; }
body.skin-majesty .ww-q-row.ww-q-past .ww-q-desc { font-size: 15px; }
/* ── THE PLAY DOOR'S MODE LIST (#2557, 2026-09-07) ────────────────────
   Deliberately the same object as a room row in the Rooms window: a wide
   pressable line, the name in the tome, a quiet line under it, and what
   happens at the right end. Two windows that both answer "where do I go
   next" should not invent two shapes for it. */
body.skin-majesty .ww-modes { display: flex; flex-direction: column; gap: 6px; }
/* ⛔THE ACTION GOES UNDER THE DESCRIPTION, NOT BESIDE IT (2026-09-09).
   A `nowrap` label centred against a paragraph that wraps reads as the end of
   the sentence: measured at 410px, "Deploy a Nine with three cards and" sat on
   one line with "Pick a zone" 12px to its right, so the row read as "Deploy a
   Nine with three cards and Pick a zone". Narrowing the window into its band
   made it WORSE, not better, because there was less room to wrap in -- the
   side-by-side arrangement was always going to lose to a long enough line.
   Stacked, it cannot collide at any width or with any copy. */
body.skin-majesty .ww-mode {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  width: 100%;
  text-align: left;
  padding: 10px 10px 11px;
  cursor: pointer;
  border: 0;
  background: rgba(122, 90, 56, .10);
  border-bottom: 2px solid rgba(122, 90, 56, .30);
  color: #2e2113;
}
body.skin-majesty .ww-mode:hover { background: rgba(122, 90, 56, .20); }
body.skin-majesty .ww-mode:focus-visible { outline: 2px solid var(--sk-gold-hi); outline-offset: -2px; }
body.skin-majesty .ww-mode-t { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
body.skin-majesty .ww-mode-t b {
  font-family: var(--sk-tome);
  font-size: 18px;
  /* ⭐THE PAPER INKS ARE LITERALS IN THIS FILE, not --sk-ink. That token is
     #f0e2c6, cream, which is right on the navy the skin started as; the
     sheet went parchment and every rule inside it writes #2e2113 by hand.
     Measured on the first cut of this list: the "Pick a zone" label was
     --sk-gold-hi (#caa982), a light gold picked for navy, and it was barely
     visible on the page. */
  color: #2e2113;
}
body.skin-majesty .ww-mode-t i {
  font-style: normal;
  font-family: var(--sk-body);
  font-size: 17px;
  line-height: 1.25;
  color: #5a4328;
}
body.skin-majesty .ww-mode-go {
  flex: none;
  align-self: flex-end;      /* the far end of its own line, where a verb belongs */
  font-family: var(--sk-head);
  font-size: 8px;
  letter-spacing: 1px;
  color: #8a6420;
  white-space: nowrap;
}
body.skin-majesty .ww-mode-none {
  font-family: var(--sk-body);
  font-size: 17px;
  color: #5a4328;
  padding: 4px 2px 0;
}

/* ⭐WHO IS ONLINE, AT THE TOP OF THE ROOMS WINDOW (2026-09-07). Two numbers
   that used to be buttons in the bottom bar; the window that lists every room
   and its count is where the total belongs. A summary line, not a heading:
   the number is the loud part and the words are quiet after it. */
body.skin-majesty .ws-room-sum {
  font-family: var(--sk-body);
  font-size: 18px;
  color: #5a4328;
  padding: 0 2px 8px;
  border-bottom: 2px solid rgba(122, 90, 56, .35);
  margin-bottom: 8px;
}
body.skin-majesty .ws-room-sum b {
  font-family: var(--sk-head);
  font-size: 11px;
  color: #8a6420;
  margin-right: 6px;
}
body.skin-majesty .ws-room-sum i {
  font-style: normal;
  color: #5a4328;
  float: right;
}
/* the handles under the room they are standing in -- the popover's content,
   in the list rather than behind a press */
body.skin-majesty .ws-room-who {
  font-family: var(--sk-body);
  font-size: 17px;
  line-height: 1.25;
  color: #5a4328;
  padding: 0 4px 8px 10px;
  margin-top: -4px;
  word-break: break-word;
}
body.skin-majesty .ws-navrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  border: 0;
  border-bottom: 1px solid rgba(46, 33, 19, .25);
  background: none;
  padding: 8px 6px;
  cursor: pointer;
  text-align: left;
  color: #2e2113;
}
body.skin-majesty .ws-navrow:hover { background: rgba(162, 129, 85, .18); }
body.skin-majesty .ws-navrow.is-here { background: rgba(202, 169, 130, .22); }
body.skin-majesty .ws-nav-t { display: flex; flex-direction: column; min-width: 0; }
body.skin-majesty .ws-nav-t b {
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 20px; font-weight: 400; line-height: 1.1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.skin-majesty .ws-nav-t i {
  font-style: normal;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px; color: #5a4328; line-height: 1.2;
}
body.skin-majesty .ws-nav-n {
  flex: 0 0 auto;
  display: flex; align-items: baseline; gap: 4px;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px; color: #5a4328;
}
body.skin-majesty .ws-nav-n b { font-size: 19px; font-weight: 400; color: #8a5a1f; }
body.skin-majesty .ws-nav-n i {
  font-style: normal; color: #a4343a; margin-left: 6px;
}

/* the navigator is ROWS, so the old two-column .ws-rooms grid gives way */
body.skin-majesty .ws-rooms { display: block; }

/* ── THE FRIENDS TAB (#2514) ─────────────────────────────────────────────
   A friend row is the navigator's row with buttons where the count goes, so
   it wears `.ws-navrow` and only says what differs. It is a DIV, not a
   button: the row itself does nothing, the planks on it do, and a pointer
   cursor on a dead surface is the same lie as a dead control. */
body.skin-majesty .ws-friend { cursor: default; }
body.skin-majesty .ws-friend:hover { background: none; }
body.skin-majesty .ws-friend-acts {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}
/* the plank, at the size a row can carry. The 44px tap FLOOR is untouched --
   #2537 bought it and #2602 already spent some of it on the ninth tile -- so
   this narrows the padding and the type, never the box. */
body.skin-majesty .wr-plank.ws-friend-b {
  font-size: 9px;
  padding: 0 6px 2px;
}

/* ASK BY HANDLE. The well is the chat input's, because a hole you type into
   should look the same everywhere in this world (world-skin.css:1739). */
body.skin-majesty .ws-friend-add {
  display: flex;
  gap: 6px;
  align-items: stretch;
  padding: 2px 2px 10px;
}
body.skin-majesty .ws-friend-in {
  flex: 1 1 0;                 /* not `1 1 auto`: a long handle makes the
                                  basis enormous and collapses the column */
  min-width: 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 19px;
  line-height: 1;
  color: var(--sk-ink);
  background: rgba(46, 33, 19, .16);
  border: 0;
  box-shadow: inset 0 0 0 1px rgba(46, 33, 19, .32);
  border-radius: 0;
  padding: 0 8px;
}
body.skin-majesty .ws-friend-in:focus {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--sk-gold);
}
body.skin-majesty .ws-friend-in::placeholder { color: #5a4328; }

/* the SUPERSEDED fold-out still parks its slot wrapper inside the plate
   (world-equip builds it at mount); it was the invisible third child holding
   a 36px hole between banner and bust. The module deletes whole once Wray
   signs the character window off; until then its furniture does not get to
   shape the HUD.

   ⭐KEYED ON THE PLATE, NOT ON THE RAIL (2026-09-06). This read
   `.ws-rail[data-edge='left'] .ws-plate .we-slotwrap` for as long as the
   plate lived in the left rail. The Albion nav MOVES the plate into its own
   top-left corner (world-albion-nav.js adopts the node), so the selector
   stopped matching and the retired panel was one drag away from unfolding
   over the HUD -- which is what Wray photographed: "notice in the top left
   the old profile slots appear after you open your character sheet." The
   wrapper only ever exists inside the plate, so keying it there hides it
   wherever the plate is put. world-equip.js refuses to open it as well; a
   thing this retired should be shut by both halves. */
body.skin-majesty .ws-plate .we-slotwrap { display: none; }

/* ── NERM IN THE BOTTOM BAR (Wray, 2026-09-05). The scrap goes horizontal:
   face left, line beside it, parchment behind, sized to the band. He reads
   as dialogue on a bar that is becoming buttons + stats + dialogue. */
body.skin-majesty #wt-foot .wt-nerm {
  flex: 0 1 340px;
  min-width: 180px;
  display: flex;
  gap: 8px;
  align-items: center;
  background: url('/assets/ui/paper.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
  padding: 6px 10px;
  box-shadow: 0 3px 0 rgba(0, 0, 0, .5);
  margin-left: auto;           /* rides the right end of the bar */
}
body.skin-majesty #wt-foot .wt-face {
  width: 34px; height: 34px; flex: 0 0 auto;
  background: url('/assets/ui/nerm.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
}
body.skin-majesty #wt-foot .wt-line {
  margin: 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px; line-height: 1.2; color: #2e2113;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* ── THE BAG'S GRID SITS ON PARCHMENT, rarity ribbons over its top edge
   (Wray, 2026-09-05: "the item slots can sit on a parchment and the rarity
   ribbons flow over the top"). Same paper nine-slice as every window; the
   ribbons hang half off the sheet the way the journal's marks hang off the
   panel. The old inline type chips are gone -- types are the door's RIGHT
   strip now. */
body.skin-majesty .ww-bagwrap {
  position: relative;
  /* ⭐NO SECOND PARCHMENT (Wray, 2026-09-07): "bag window had parchment
     within parchment which might be causing the cramping issue" -- and he is
     right on both counts.

     This drew `paper.png` as a 32px nine-slice border INSIDE a window whose
     ground is already that same paper. That is the one-surface ruling
     contradicted from the inside, the same way framed pages were before
     #2588: Wray, 2026-08-29, "menus with a window and parchment inside its
     too much". So removing it applies a ruling that already existed rather
     than making a new call.

     ⛔AND IT WAS THE CRAMPING, MEASURED. 32px each side took the wrap's 412
     down to 348 usable. That is why the cell budget was (340 - 21) / 8 =
     39.8px, why 40 was the ceiling at a 2.5x upscale of 16px art, and why
     the eighth column was clipped. Without it the grid gets the full 412,
     which is what lets --sk-cell be a whole 3x multiple below. */
  image-rendering: pixelated;
  /* ⛔NO SIDE PADDING: THE GRID WAS CLIPPING BY A PIXEL BEFORE ANYONE TOUCHED
     IT. This was `0 4px 4px`. The 32px paper border each side leaves 348 of
     the wrap's 412, and 4px of padding each side took that to 340 -- while a
     row of 8 cells at --sk-cell 40 with 3px gaps needs 341. So the eighth
     column's right edge was cut, on every screen, since the window narrowed.
     Measured at 1512x831: clientW 348, the grid's own box 341. Dropping the
     side padding gives it 348 and 7px of slack; the bottom 4px stays, because
     nothing was tight there. */
  padding: 0 0 4px;
  margin-top: 14px;            /* room for the ribbons to hang over the edge */
}
body.skin-majesty .ww-rarerow {
  position: absolute;
  top: -26px;
  left: 18px;
  display: flex;
  gap: 6px;
  z-index: 2;
}
body.skin-majesty .ww-rare {
  border-style: solid;
  border-image-source: url('/assets/ui/ribbon-closed-h.png?v=2026-08-29d');
  border-image-slice: 0 4 0 5 fill;
  border-image-width: 0 8px 0 10px;
  border-width: 0 8px 0 10px;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  height: 24px;
  padding: 0 6px 2px;
  cursor: pointer;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  line-height: 1;
  color: #f6ecd6;
  text-transform: capitalize;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
}
body.skin-majesty .ww-rare[aria-pressed='true'] {
  border-image-source: url('/assets/ui/ribbon-open-h.png?v=2026-08-29d');
  color: #2e2113;
  text-shadow: none;
  /* the chosen rarity says WHICH one in its own colour, not colour alone --
     the ribbon art change is the primary signal */
  box-shadow: inset 0 -3px 0 var(--rc, #9d8a6d);
}
/* ink flips: the cells now sit on paper, not navy */
body.skin-majesty .ww-bagwrap .ww-foot { color: #5a4328; }

/* ── THE CHARACTER WINDOW STANDS ALONE (2026-09-05): parchment of its own,
   one layer above the door sheet, so the bag can be open beside it. */
body.skin-majesty .wc-window {
  position: fixed;
  z-index: 61;
  /* CLAMPED TO THE PICTURE (#2557 finding 2): 480 wide on a 390 phone opened
     at x=-15 with both edges cut and the close off-screen. Centred on the
     width it actually draws at (480, set below) -- the old -210 was half of a
     420 this rule no longer wins. */
  /* THE DOCK (#2557): this is Albion's Appearance panel, and it opens on
     the LEFT so the bag can be open on the right beside it -- the pair the
     equip flow (world-equip.js) already opens together. 8px in, 16px down,
     covering the plate the way Albion's left panels cover that corner. */
  left: calc(var(--fx, 0px) + 8px);
  /* ⭐96, THE SAME LINE AS EVERY DOCKED SHEET (2026-09-12). This window kept
     the 16 the left dock started with, so it still covered the player plate
     (12,12-342,68) after #2693 moved every sheet down to clear it -- the one
     left panel that still drew its title across your own name. Same top,
     same band, same bottom edge as the Rooms panel beside it. Width stays
     600: the doll and its stat column are a two-column layout that the
     .24 share cannot hold, and the window's retirement is #2681's call. */
  top: calc(var(--fy, 0px) + 96px);
  /* 600 since the two-column pick (#2557, 2026-09-06); the phone clamp is the same */
  width: min(600px, calc(var(--fw, 100vw) - 16px));
  max-height: calc(var(--fh, 100vh) - var(--sk-foot) - 96px - 8px);
  display: flex;
  flex-direction: column;
  border-style: solid;
  border-image-source: url('/assets/ui/paper.png?v=2026-08-29a');
  border-image-slice: 16 fill;
  border-image-width: 32px;
  border-width: 32px;
  border-image-repeat: stretch;
  image-rendering: pixelated;
  color: #2e2113;
  box-shadow: 0 8px 0 rgba(0, 0, 0, .45);
}
body.skin-majesty .wc-window[hidden] { display: none; }
body.skin-majesty .wc-winhead {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-top: -14px;
}
body.skin-majesty .wc-wintitle {
  position: absolute;
  left: 50%;
  top: -12px;
  transform: translateX(-50%);
  border-style: solid;
  border-image-source: url('/assets/ui/title-banner-red.png?v=2026-08-30b');
  border-image-slice: 0 16 0 16 fill;
  border-image-width: 0 32px;
  border-width: 0 32px;
  border-image-repeat: stretch;
  image-rendering: pixelated;
  height: 34px;
  min-width: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sk-tome);
  font-size: 22px;
  color: #e8d5a9;
  padding: 0 8px 6px;
}
body.skin-majesty .wc-winclose {
  border-style: solid;
  border-image-source: url('/assets/ui/ribbon-closed.png?v=2026-08-29b');
  border-image-slice: 5 0 4 0 fill;
  border-image-width: 10px 0 8px 0;
  border-width: 10px 0 8px 0;
  border-image-repeat: repeat;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  width: 30px;
  height: 46px;
  margin-right: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 8px 12px;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 10px;
  color: #f6ecd6;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .45);
  cursor: pointer;
}
body.skin-majesty .wc-winclose:hover {
  border-image-source: url('/assets/ui/ribbon-open.png?v=2026-08-29b');
  color: #2e2113;
  text-shadow: none;
}
body.skin-majesty .wc-winbody {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 6px 4px 8px;
}
body.skin-majesty .wc-winbody::-webkit-scrollbar { width: 0; }
/* the sheet's parts re-ink for parchment: this window is paper, the door
   version was navy */
body.skin-majesty .wc-window .wc-num { color: #2e2113; }
body.skin-majesty .wc-window .wc-stat b { color: #5a4328; }
body.skin-majesty .wc-window .wc-skills { color: #5a4328; }
body.skin-majesty .wc-window .wc-trayhead { color: #2e2113; }
body.skin-majesty .wc-window .wc-stats {
  border-color: rgba(46, 33, 19, .3);
  flex-wrap: wrap;         /* five stats in 420px: LUCK was clipping */
  gap: 6px 14px;
}

/* a leaderboard row wearing an fx banner: the canvas is the ground, every
   word stands above it, and the row clips its own weather */
body.skin-majesty .ww-rank.ww-fxrow { overflow: hidden; }
body.skin-majesty .ww-rank.ww-fxrow > :not(.fx9-canvas) {
  position: relative;
  z-index: 1;
}

/* ── THE BAR STOPS COVERING THE WORLD (Wray, 2026-09-05: "description for
   target is still behind that bottom rail. so maybe the side rails sit
   ontop of the bottom bar?"). It did: the foot painted at z-index 55 while
   the rails sit at 38 and the target pane at 41, so ANY overlap hid them.
   Two fixes, because either alone is a half:
     · the bar drops under the chrome it must never cover, staying above
       the render;
     · and --sk-foot above is what the rails and the target pane measure
       against, so with the bar's real height in it there is no overlap to
       arbitrate in the first place. */
body.skin-majesty #wt-foot {
  z-index: 36;
  gap: 14px;
}

/* ── THE SAY LINE, IN THE THIN STRIP ──────────────────────────────────── */
body.skin-majesty #wt-foot #world-chat.wp-in-rail {
  flex: 0 1 460px;
  min-width: 200px;
  height: 30px;
  align-self: center;
}
body.skin-majesty #wt-foot #world-chat.wp-in-rail input { font-size: 16px; }

/* ── THE HISTORY, UP THE LEFT RAIL ────────────────────────────────────
   Pinned to the rail's foot with margin-top:auto and grown UPWARD by its
   own height knob -- which is the whole of "the chat needs to be able to
   go up the left of the screen and sit at the bottom". The bar's height is
   no longer its ceiling, because the bar is no longer its home. */
body.skin-majesty .ws-rail[data-edge='left'] .wp-chatpanel {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 0;
}
body.skin-majesty .ws-rail[data-edge='left'] .wp-log {
  flex: 0 0 auto;
  /* 214 is his (`.wp-log` height, same save) */
  height: var(--sk-chatlog-h, 214px);
  max-height: none;
  min-height: 60px;
}

/* ── NERM IN THE BAND between the picture and the strip ───────────────── */
body.skin-majesty .wt-nerm {
  position: fixed;
  left: calc(var(--fx, 0px) + 12px);
  bottom: calc(var(--sk-foot) + 4px);
  width: 320px;
  max-width: calc(var(--fw, 100vw) - 24px);
  z-index: 40;
  display: flex;
  gap: 8px;
  align-items: center;
  background: url('/assets/ui/paper.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
  padding: 6px 10px;
  box-shadow: 0 3px 0 rgba(0, 0, 0, .5);
}
/* and he steps to the right of an open left window (2026-09-12) -- the
   ruling and the seam are on the left-dock block; this is one of its three
   consumers */
body.skin-majesty.ws-left-open .wt-nerm {
  left: calc(var(--ws-left-edge, 0px) + 16px);
}
/* and he stands above the v2 action bar on a phone (#3069 S4, behind the
   HUD pass gate). Wider than 707 that bar keeps right of this strip
   (world-actionbar.css --ab-clear-l); narrower it spans the screen and sat
   on him whole (z 42 over his 40). Same height as the guide's bubble
   (nerm-guide.css): the bar's measured height, the plank above it, air. */
@media (max-width: 707px) {
  body.skin-majesty.hud-v2:has(#w0worldbar.ab2) .wt-nerm {
    bottom: calc(var(--w0bar-h, 110px) + 68px);
  }
}
body.skin-majesty .wt-face {
  width: 34px; height: 34px; flex: 0 0 auto;
  background: url('/assets/ui/nerm.png?v=2026-08-29a') 0 0 / 100% 100% no-repeat;
  image-rendering: pixelated;
}
body.skin-majesty .wt-nerm .wt-line {
  margin: 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 19px; line-height: 1.2; color: #2e2113;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* ── THE RIGHT RAIL RUNS LONGER, so the news window can grow into it
   (Wray, 2026-09-05: "make the right rail longer so i can extend the news
   window"). With the strip thin again the column already gains that height;
   the journal gets a knob of its own so he can claim it rather than wait
   for the quest list to fill it. */
/* 210: he dragged the news window to 211 in the lab, so it claims that
   height rather than waiting for quests to fill it */
body.skin-majesty .wt-journal { min-height: var(--sk-journal-h, 210px); }

/* ── ROOMS IS A RIBBON. FULL STOP. ───────────────────────────────────
   Third report (Wray, 2026-09-05): "the rooms ribbon still much bigger and
   doesnt look like it belongs to the same family of ribbons on the left."

   Twice I heard "banner" and reached for size; both times the size IS what
   broke the family, because a nine-sliced ribbon's caps are drawn at a
   fixed scale and its face is a 15px bitmap -- stretch the box and the ends
   and the type stop matching every neighbour. There is no "bigger version"
   of this object that still looks like it.

   So it is the door ribbon, unchanged, at 36px like the rest. What makes it
   the way OUT rather than a seventh page is POSITION and SPACE: first, with
   a gap under it separating it from the pages below. That is the whole
   difference, and it is the one that costs nothing. */
body.skin-majesty .ws-rail.wrt-on .wrt-banner {
  order: -1;
  margin-bottom: 12px;
}

/* ── FOLDED FROM THE LAB SAVE (audit/lab, 2026-09-04T17:37Z) ───────────
   Wray pinned the SAME title-banner geometry on five separate doors and the
   same close-ribbon nudge on all five: 240x60 and up, close up ~34 and in
   ~14. Five identical per-door pins is not five decisions, it is one
   decision he had to repeat because the family was unreachable by clicking
   (fixed since, #2379). So it folds into the base rule where he meant it.

   NOT FOLDED, and why: his `#wt-frame` pin resized the picture frame to
   970x730. The frame's size IS the render rectangle the engine publishes;
   pinning it decouples the two, and the next resize puts the uprights
   somewhere the picture is not. And his `.wp-chatpanel` nudge (up 49) and
   `.wt-nerm` move (left 455) were both him hand-fixing the bar overlap that
   this change fixes properly. */
body.skin-majesty .ws-sheet-title {
  min-width: 240px;
  height: 60px;
  /* -50% IS THE CENTRING (Wray, 2026-09-05: "the position of the window
     title ribbons moved, can you centre them for every window"). The base
     rule centres with `left:50%; translate:-50% 0`, and folding his lab
     pin as `translate: 0 -14px` overwrote the whole property -- so every
     title lost its -50% and sat half its own width to the right. The
     y-nudge has to travel WITH the -50%, never instead of it. */
  /* ⭐ON THE TOP EDGE, NOT UNDER IT (Wray, 2026-09-08: "the banner needs
     nudging up a little so the banner sits on the top edge"). Measured: the
     banner's top sat 8px above the sheet's edge and 52px below it, so it read
     as printed on the page rather than pinned across its edge.

     ⛔AND -36 STRADDLED THE BOX, NOT THE BANNER. title-banner-red.png is 89x20
     and its BOTTOM FOUR ROWS ARE FULLY TRANSPARENT -- read off the art, column
     44: the dark outline is y0, the lit red band is y1..y12, the shadow lip is
     y13..y15, and y16..y19 are alpha 0. So a 60px box draws 48px of banner and
     12px of nothing, and an even split of the BOX put 30 above the edge and
     only 18 of the drawn art below. -30 straddles what you can actually see:
     24 above, 24 below. */
  translate: -50% -30px;
  /* THE TEXT FILLS THE BAND ("bump the text to fill the ribbon too"). The
     art is 89x20 with a 4px shadow lip, so at 60px the lit band is 48 and
     the lip is 12 -- the padding is that lip, and 32px is the tome's whole
     step that fills 48 without touching it.
     ⭐AND THE PADDING IS SOLVED, NOT GUESSED. The lit band is art rows y1..y12
     of 20, which at 3x is 3px..39px -- centre 21. Measured the other way, the
     tome's ink for this string runs 26px tall (ascent 18, descent 8) inside a
     32px line box. Solving for an ink centre of 21 gives a bottom pad 18
     greater than the top, so 0 and 18. A previous 4/12 put the ink centre at
     26: five pixels low, which is what Wray kept seeing. */
  font-size: 32px;
  padding: 0 10px 18px;
}
/* ⛔-20, NOT -16. The margin above moved it UP 4 and this moved it DOWN 4 in
   the same change, so it stayed exactly where it was: measured 4px below the
   sheet's top edge before and after. The two numbers have to be read together
   -- which is the argument for measuring rather than reasoning about a nudge. */
body.skin-majesty .ws-sheet-top .wr-plank { translate: 12px -20px; }
/* the tap floor, kept exactly where it is for: a coarse pointer */
@media (pointer: coarse) {
  body.skin-majesty .ws-sheet-top .wr-plank { min-width: 44px; }
}
body.skin-majesty .wt-journal { translate: 0 -20px; }

/* Nerm's line: his pin asked for 25 on the old right-rail scrap; 19 in the
   band, where two clamped lines have to fit the tray's height. */

/* ── THE NAVIGATOR GETS COMPACT (Wray, 2026-09-05: "needs to be more
   compact because there will be more than just these rooms and needs to be
   easier to find players rooms at a browse"). Rows lose a third of their
   height, the sub line drops to one clipped line, and the count column
   keeps its weight -- browsing is scanning, and a scan wants more rows in
   the same glance. */
body.skin-majesty .ws-navrow { padding: 5px 6px; gap: 10px; }
body.skin-majesty .ws-nav-t b { font-size: 18px; }
body.skin-majesty .ws-nav-t i {
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 46ch;
}
body.skin-majesty .ws-nav-h { padding: 0 2px 4px; font-size: 15px; }

/* ── A DOWN-HANGING RIBBON ON THE CHAT HISTORY (Wray, 2026-09-05, asked
   twice). The same mark the journal hangs off its panel, so the history
   reads as a page of the world's chrome rather than a dark box: the channel
   row IS that ribbon now, hanging off the log's top edge. */
body.skin-majesty .ws-rail[data-edge='left'] .wp-chans {
  position: relative;
  z-index: 2;
  margin-bottom: -6px;
  padding-left: 8px;
}
body.skin-majesty .ws-rail[data-edge='left'] .wp-chan {
  border-image-source: url('/assets/ui/ribbon-closed.png?v=2026-08-29b');
  /* THE JOURNAL'S RECIPE, NOT A NEW ONE. The vertical cut is 15x39 and its
     text runs DOWN it -- that is what a hanging mark is in this skin, and
     the .wt-mark rules beside the quests panel are the proven version. Used
     horizontally at 54px wide the art stretched 3.6x and the label clipped
     to "ca", which is the same made-it-up mistake as the Rooms banner. */
  border-image-slice: 5 0 4 0 fill;
  border-image-width: 10px 0 8px 0;
  border-width: 10px 0 8px 0;
  border-image-repeat: repeat;
  width: 30px;
  height: 62px;
  padding: 0 0 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  writing-mode: vertical-rl;
  font-size: 14px;
  letter-spacing: .06em;
  /* the art's face sits right of centre in a 15px column, same as the
     journal's marks: half the leftover walks the text onto it */
  padding-left: 8px;
}
body.skin-majesty .ws-rail[data-edge='left'] .wp-chan.is-on {
  border-image-source: url('/assets/ui/ribbon-open.png?v=2026-08-29b');
}

/* ── THE HERO PORTRAIT (Wray, 2026-09-05: "top left sprite in the character
   window is tiny, nothing like the hero portrait that can carry a skeletal
   system"). It was 128 in a 420px window. At 200 it is the sheet's subject
   rather than a stamp beside its slots, and it is the size the decay render
   needs to read at all -- nine-portrait draws wounds per bitmap cell, so a
   small canvas loses them before the skeleton beneath ever shows. The
   under-layer art itself is still owed (#2375). */
body.skin-majesty .wc-portrait {
  width: 200px;
  height: 200px;
}
body.skin-majesty .wc-window { width: min(600px, calc(var(--fw, 100vw) - 16px)); }

/* the left strip sizes to its words too, or "Player rooms" clips to
   "Player ro" against the fixed 104 the doors were drawn at */
body.skin-majesty .ws-tabs:not(.ws-tabs-r) .wr-plank {
  width: auto;
  min-width: 104px;
}
body.skin-majesty .ws-tabs:not(.ws-tabs-r) .wr-plank[aria-current='page'] {
  width: auto;
  min-width: 156px;
}

/* ── THE ITEM PREVIEW GROWS UP (#2397) ─────────────────────────────────
   Wray asked for "a bitmap card larger preview window... paired with
   hovering over smaller icons, hold shift to compare the stat gains to
   current equipt item." The hover card WAS the preview already; it was
   just small and said nothing about what you are wearing. So it takes the
   parchment every readable surface in this skin takes, gets room to hold a
   card's worth of reading, and grows a compare block.

   Not a second card component: the house rule is that anything rendering
   a card reuses the canonical one, and this is the bag's own tooltip
   grown rather than a rival to it. When the bitmap item-card renderer is
   wired in (item-card-v4), it drops INTO this box. */
body.skin-majesty .ww-hover {
  max-width: 300px;
  min-width: 220px;
  padding: 0;
  background: none;
  border: 0;
  border-style: solid;
  border-image-source: url('/assets/ui/paper.png?v=2026-08-29a');
  border-image-slice: 16 fill;
  border-image-width: 26px;
  border-width: 26px;
  border-image-repeat: stretch;
  font-size: 17px;
  color: #2e2113;
}
body.skin-majesty .ww-hover > b {
  display: block;
  font-family: var(--sk-tome);
  font-size: 22px;
  line-height: 1.1;
  margin-bottom: 2px;
}
body.skin-majesty .ww-hover > i {
  display: block;
  font-style: normal;
  color: #5a4328;
  text-transform: capitalize;
  margin-bottom: 4px;
}
/* the compare block: separated by a rule so it reads as a second thing
   about a different item, not more facts about this one */
body.skin-majesty .ww-cmp {
  margin-top: 6px;
  padding-top: 5px;
  border-top: 1px solid rgba(46, 33, 19, .28);
}
body.skin-majesty .ww-cmp-hint { font-style: normal; color: #8a7355; font-size: 15px; }
body.skin-majesty .ww-cmp-h {
  display: block;
  font-weight: 400;
  color: #5a4328;
  margin-bottom: 2px;
}
/* up and down carry a sign as well as a colour, because colour alone is
   the anti-pattern and these are the numbers the whole feature is for */
body.skin-majesty .ww-cmp .is-up i { color: #2f7d32; }
body.skin-majesty .ww-cmp .is-down i { color: #a4343a; }

/* the zoom pad, once it lives in the strip: the module's own rule pins it
   `position: fixed` against the render rect, and every property that
   placement set has to be answered or half of it lingers (the lesson the
   chat bar taught at #world-chat) */
body.skin-majesty .wcs-frame.wcs-in-foot {
  position: static;
  left: auto; top: auto; right: auto; bottom: auto;
  width: auto; height: auto;
  align-self: center;
  margin-left: auto;      /* rides the strip's right end, past the counts */
}
body.skin-majesty .wcs-frame.wcs-in-foot .wcs-pad {
  position: static;
  display: flex;
  gap: 6px;
  margin: 0;
}
body.skin-majesty .wcs-frame.wcs-in-foot .wr-plank {
  width: 30px;
  height: 26px;
  padding: 0 0 4px;
  font-size: 14px;
}

/* ══════════════════════════════════════════════════════════════════════
   THE SPELLBOOK'S CARDS PAGE (Wray, 2026-09-04; the build row left
   2026-09-24, #3031 "A")

   The collection, and one line above it saying where cards go: into gear,
   on the bag's doll, with a plank that opens the bag. The three `.we-build`
   slots that wrote the old arena's loadout are gone, and so are their rules.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-spells {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
  gap: 6px;
}
body.skin-majesty .ww-spellsnote {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 12px;
  margin-top: 6px;
  text-align: center;
}
body.skin-majesty .ww-spellsnote p {
  margin: 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 17px;
  line-height: 1.2;
  color: #6b5233;
}
body.skin-majesty .ww-spellsgo { min-height: 44px; }

/* THE CHARACTER SHEET'S BUILD ROW TAKES A DROP TOO (Wray: "they can be on
   both surfaces"). Same engine, same write, so it gets the same rim on
   hover -- a target that lights differently on two surfaces reads as two
   different mechanics. */
body.skin-majesty .wc-bslot.we-build { cursor: copy; }
body.skin-majesty .wc-build .we-build.is-over {
  opacity: 1;
  outline: 2px solid var(--sk-gold-hi, #f4d58a);
  outline-offset: -2px;
}
body.skin-majesty .wc-buildsay {
  margin: 2px 0 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  min-height: 16px;
  color: #8a3324;
}
/* the way to the cards, since this sheet deliberately does not list them */
body.skin-majesty .wc-tocards {
  align-self: flex-start;
  margin: 0 0 2px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  color: #6b5233;
  text-decoration: underline;
  text-underline-offset: 2px;
}
body.skin-majesty .wc-tocards:hover { color: #4a3216; }

/* ══════════════════════════════════════════════════════════════════════
   THE MAJESTY PLANK IS THE BUTTON (Wray, 2026-09-06)
   ──────────────────────────────────────────────────────────────────────
   "the kit should be using the majestic kit not the leather buttons ...
   the comfy tap safe buttons in the leather chunky i dont want to keep."

   The world had two buttons. `.wr-plank` in world-rail.css is a brown
   leather gradient that this skin never redressed, and the navy-and-brass
   nine-slice (btn-idle / on / bright / press) is what the rail tiles, the
   wallet chip and the camera pad already wore. Wray liked the second and
   the kit called the first "canonical" three times.

   So under this skin EVERY plank wears the nine-slice. The rules that
   already dress a plank into something else are more specific and keep
   winning: the red cloth ribbons on `.ws-tabs`, the close ribbon on
   `.ws-sheet-top`, the chat's Say. This only replaces the leather that
   showed through everywhere nothing more specific had been written.

   ⭐THE FRAME SCALES WITH THE RENDER, AND THAT IS WHAT MAKES IT COMFY.
   Every earlier copy of this art hard-coded the frame at 16/20/20/20: a 4x
   upscale of the 4/5 slice while the world drew at 2x, so the button was
   drawn at twice the chrome pixel and read as a slab. #2020 again: comfy
   versus chunky is SIZE, not art. One art pixel per chrome pixel here,
   with a floor of 2 so a x1 render never shrinks the frame under its own
   slice. The 44px floor is the #2490 tap minimum and is not negotiable. */
body.skin-majesty { --sk-btn-k: max(2, var(--fs, 3)); }

body.skin-majesty .wr-plank {
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k));
  border-width: calc(4px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k));
  border-radius: 0;
  image-rendering: pixelated;
  background: transparent;
  box-shadow: none;
  color: var(--sk-ink);
  text-shadow: none;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 10px;
  line-height: 1;
  letter-spacing: .04em;
  min-height: 44px;
  min-width: 44px;
  padding: 0 8px 2px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: none;
}
body.skin-majesty .wr-plank:hover,
body.skin-majesty .wr-plank[aria-current='page'],
body.skin-majesty .wr-plank[aria-pressed='true'] {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
  color: var(--sk-ink-on-gold);
  filter: none;
}
body.skin-majesty .wr-plank:active {
  border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a');
  transform: none;
  filter: none;
}
body.skin-majesty .wr-plank:disabled,
body.skin-majesty .wr-plank[aria-disabled='true'] {
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  color: var(--sk-ink-dim);
  opacity: .55;
}
body.skin-majesty .wr-plank:focus-visible { outline: 2px solid var(--sk-gold); outline-offset: 2px; }

/* THE CHIP, READABLE. Wray: "the chip buttons are nice but the text is way
   too small on them." It was 8px Press Start 2P inside a 52px frame that
   was itself drawn at 4x: almost all chip and almost no reading. Same
   scaled frame as the plank, and the type at the size a number the eye
   goes to deserves. Height follows the content instead of being pinned. */
body.skin-majesty .ws-chip {
  border-image-width: calc(4px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k));
  border-width: calc(4px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k)) calc(5px * var(--sk-btn-k));
  height: auto;
  min-height: 44px;
  padding: 0 10px 2px;
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: .04em;
}

/* ══════════════════════════════════════════════════════════════════════
   THE DOCK, THE LEFT SIDE (#2557, 2026-09-06)
   ──────────────────────────────────────────────────────────────────────
   world-shell.js stamps `data-side` on the sheet and `data-side` +
   `data-edge` on both strips when a door opens. The sheet's default is the
   right edge (declared in its own rule above); this is the other one.
   ══════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════
   ⭐THE THIRD PLACEMENT: CENTRED (#2020, Wray 2026-09-09)
   ──────────────────────────────────────────────────────────────────────
   "Albions store... yes it opens a central window so your right lets get that
   matching our store page."

   #2557 measured the rule off the reference and this is the half we had not
   built: a NARROW panel docks to an edge, a WIDE browsing window floats
   centred. Albion's marketplace is 42.5% x 71.1% in the middle of the screen;
   ours was 27% x 88% jammed against the left rail, which is the shape of a
   list, not of a table you read across.

   ⛔AND THE BANNER WAS OFF THE SCREEN. Measured on the Store before this:
   `[data-side='left']` sets `--sk-win-top: 16px` and the title banner hangs 24
   above the sheet, so it drew at y=-12 and was clipped by the viewport. That
   was true of EVERY left-docked door, and it is why centred windows take the
   96px top the right-docked ones use rather than 16.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ws-sheet[data-side='centre'],
body.skin-majesty .ws-tabs[data-side='centre'] {
  --sk-win-top: 96px;
}
body.skin-majesty .ws-sheet[data-side='centre'] {
  left: calc(var(--fx, 0px) + (var(--fw, 100vw) - var(--sk-win-cw)) / 2);
}

/* ⭐THE CENTRED WINDOW WEARS `screen.png` (Wray, 2026-09-09: "there is another
   frame in the majestic set, im wondering how that looks instead... i dont
   want to go browner but there was another frame i think looks nicer in that
   pack").
   
   The pack has exactly TWO screens -- `Scalable Paper.png` and
   `scalable_screen_2.png` -- so the one he means is the second, which is this
   file. A dark navy panel with a thin gold border and decorative corner
   brackets, where panel-well is a plain textured slab.
   
   ⛔THIS IS NOT REVERSING HIS LETTER A. That ruling retired screen.png from
   the DOORS: "one material, every window is the parchment" (#2607), and it was
   right, because every window then was a docked panel. The centred kind did
   not exist yet, and its material is measured separately -- Albion's centred
   marketplace has a dark frame where its docked inventory is parchment. The
   plate comes back for the kind it was never ruled out of.
   
   12 at 36px is 3x, the same whole multiple it wore before it was removed --
   recovered from the commit that removed it rather than guessed. */
/* ⭐A CENTRED WINDOW DIMS THE ROOM (Wray, 2026-09-09: "The store pages in
   albion take the centre of the screen and dim the rest to keep focus on the
   purchases").
   
   ⛔THIS DOES NOT REVERSE THE NO-DIM RULING, IT SCOPES IT. Wray, 2026-08-29:
   "they shouldn't dim the rest of the screen they should just appear in the
   space" -- and that stands for every DOCKED window, which is what existed
   then. The reference draws the same line: Albion's docked inventory leaves
   the world lit and its centred marketplace dims it. A docked panel is
   something you glance at while standing in a room; a centred one is
   something you have stopped to do.
   
   A SPREAD SHADOW, NOT AN OVERLAY. The no-dim comment records why: a
   `::before` overlay once painted on top of the window's own ground instead
   of behind it. A shadow is drawn outside the border box by construction and
   cannot make that mistake. */
body.skin-majesty .ws-sheet[data-side='centre'] {
  box-shadow: 0 8px 0 rgba(0, 0, 0, .45),
              0 0 0 100vmax rgba(10, 8, 14, .55);
}

body.skin-majesty .ws-sheet[data-side='centre'] {
  border-image-source: url('/assets/ui/screen.png?v=2026-08-29a');
  border-image-slice: 12 fill;
  border-image-width: 36px;
  border-width: 36px;
  border-image-repeat: stretch;
  background-color: transparent;
}

/* ⭐THE LEFT SIDE RUNS TO THE FOOT LIKE THE RIGHT (Wray, 2026-09-12: "run
   the window arc so our left right and central displays are appropriate
   size to then get our info into").

   ⛔THIS REVERSES THE TRAY RESERVATION, and the reason it was there is gone.
   The left band used to subtract `--sk-tray` (50px) so a left-docked window
   stopped above Nerm's speech bubble, measured 12,992, 320x46 -- a one-line
   strip. Nerm's tutorial beats (#2385) made that a 340px-wide panel ~170px
   tall standing ABOVE the foot, and measured on the served world at
   1920x1080 the Rooms window ended at y984 while the beat panel ran y835 to
   y1005: the window already grew onto him, and the reservation bought 50px
   of empty parchment plus a left panel 46px shorter than the right one.

   So the band is one band for both sides (fh minus the foot), the left and
   right docks land on the same bottom edge, and Nerm STEPS ASIDE instead:
   while a left sheet is open the shell marks <body> `ws-left-open` and
   publishes the sheet's measured right edge as `--ws-left-edge`, and all
   three of his bottom-left forms move to the window's right: the ambient
   strip (`.wt-nerm`, below), the beat panel (world-nerm-beats.css) and the
   guide's bubble (nerm-guide.css). Albion's Loadouts panel runs 90% of the
   picture on the left and its chat sits beside it: the same arrangement. */
body.skin-majesty .ws-sheet[data-side='left'],
body.skin-majesty .ws-tabs[data-side='left'] {
  /* ⛔96, AND IT TOOK TWO GOES. 16 put the banner at y=-12, off the screen
     entirely (measured on the Store). 40 fixed that and left a second fault
     the first measurement had not looked for: the PLAYER PLATE sits at
     12,12-342,68, and a sheet starting at y40 with a banner 24px above it
     lands its title right across your own name.
     
     96 clears the plate the way the right side's 96 clears the nav row (which
     ends at y56). Both sides now start on the same line, which is also what
     stops one edge of the screen looking lower than the other. */
  --sk-win-top: 96px;
}
body.skin-majesty .ws-sheet[data-side='left'] {
  left: calc(var(--fx, 0px) + 8px);
}
/* ⭐THE TORN EDGE GOES OFF THE SCREEN (Wray, 2026-09-12: "i noticed the
   parchment edge is still visible. a window opening on the left should
   overlap off frame so there is no gap between the window and edge. same
   rule applies for panels on the right except the right edge not left").

   The sheet's parchment is a 28px nine-sliced border (paper.png at 28),
   and the 8px inset put all of it on screen: a torn edge with a strip of
   room showing past it, on the side of the window that should read as
   the screen's own edge. Under full bleed the picture IS the screen, so a
   docked panel hangs from the edge itself: its outer border is pushed 28px
   past it, the content column starts flush, and the ribbons still hang
   off the inner edge because the shell pins them to the measured
   rectangle. `frame-fill-desktop` only: in the letterbox the picture has
   a gutter beside it and the inset still means something, and a phone's
   full-width sheet (the 900px rule below) must not lose 28px of a 390px
   screen to a bleed. */
body.skin-majesty.frame-fill-desktop .ws-sheet[data-side='left'] {
  left: calc(var(--fx, 0px) - 28px);
}
body.skin-majesty.frame-fill-desktop .ws-sheet[data-side='right'] {
  left: calc(var(--fx, 0px) + var(--fw, 100vw) - var(--sk-win-cw) + 28px);
}
/* the right dock's CLOSE bookmark would otherwise sit on the bled edge:
   pull it in by what the bleed took */
body.skin-majesty.frame-fill-desktop .ws-sheet[data-side='right'] .ws-sheet-top {
  padding-right: 24px;
}

/* THE RIBBONS HANG OFF THE EDGE THAT FACES THE MIDDLE. The art was keyed on
   class -- `.ws-tabs` wore the left cap and ran out leftward, `.ws-tabs-r`
   the right cap -- which was right while every window sat in the middle and
   the two strips had one edge each. Docked, BOTH strips of a left-docked
   door hang off its right edge, so the cap is a property of the edge, not
   of which strip it is. `data-edge` says which; these rules outrank both
   class-keyed sets at (0,3,1) against their (0,2,1). */
body.skin-majesty .ws-tabs[data-edge='right'] { align-items: flex-start; }
body.skin-majesty .ws-tabs[data-edge='left']  { align-items: flex-end; }

body.skin-majesty .ws-tabs[data-edge='right'] .wr-plank {
  border-image-source: url('/assets/ui/tab-off.png?v=2026-09-03a');
  border-image-slice: 0 4 0 1 fill;
  border-image-width: 0 10px 0 3px;
  border-width: 0 10px 0 3px;
  justify-content: flex-end;
  text-align: right;
  padding-right: 13px;
  width: auto;
  min-width: 104px;
}
body.skin-majesty .ws-tabs[data-edge='right'] .wr-plank:hover { width: auto; min-width: 122px; }
body.skin-majesty .ws-tabs[data-edge='right'] .wr-plank[aria-current='page'] {
  border-image-source: url('/assets/ui/tab-on.png?v=2026-09-03a');
  width: auto;
  min-width: 156px;
}

body.skin-majesty .ws-tabs[data-edge='left'] .wr-plank {
  border-image-source: url('/assets/ui/tab-off-left.png?v=2026-09-03a');
  border-image-slice: 0 1 0 4 fill;
  border-image-width: 0 3px 0 10px;
  border-width: 0 3px 0 10px;
  justify-content: flex-start;
  text-align: left;
  /* the inner 14px of every ribbon tucks under the page; the words stop
     before it, or "headwear" reads "headwea" (measured on the bag) */
  padding-right: 16px;
  /* SIZED BY THE WORDS, like the right cap already was. A right-docked
     door's second strip -- the bag's eight filters -- now hangs on this edge
     too, and at a fixed 104 "headwear" and "expression" lost their tails
     under the tuck. The strip is align-items: flex-end, so a longer ribbon
     grows OUTWARD, away from the page, and the label rides its outer end. */
  width: auto;
  min-width: 104px;
}
body.skin-majesty .ws-tabs[data-edge='left'] .wr-plank:hover { width: auto; min-width: 122px; }
body.skin-majesty .ws-tabs[data-edge='left'] .wr-plank[aria-current='page'] {
  border-image-source: url('/assets/ui/tab-on-left.png?v=2026-09-03a');
  width: auto;
  min-width: 156px;
}

/* ON A PHONE THE SHEET IS THE SCREEN (#2560), so it hangs from the top on
   both sides and the row of doors goes under it -- there is no room beside
   it to keep clear of. */
@media (max-width: 900px) {
  body.skin-majesty .ws-sheet,
  body.skin-majesty .ws-tabs {
    --sk-win-top: 16px;
  }
}

/* ⭐ON A PHONE THE CHOSEN RIBBON DOES NOT RUN OUT (#3031, 2J). The run-out
   (104 -> 156, "unmistakably longer than its neighbours") is how a ribbon
   hanging off a window's EDGE says it is chosen; in the phone's row it only
   pushed the last ribbon past the sheet. Measured at 390x844 on the
   Leaderboard: Leaders 104, Houses 104, Guilds 156 put Guilds' right end at
   394 against a row ending at 372, so the word was cut. The row already says
   which is chosen with the tab-on art, so every ribbon here sizes to its
   words. After the edge-keyed rules above on purpose: they are (0,5,1) with
   the aria-current, and so is this, and the later one wins. */
@media (max-width: 900px) {
  body.skin-majesty .ws-tabs[data-edge] .wr-plank,
  body.skin-majesty .ws-tabs[data-edge] .wr-plank:hover,
  body.skin-majesty .ws-tabs[data-edge] .wr-plank[aria-current='page'] {
    width: auto;
    min-width: 88px;
    /* sized to its words, a label has nothing between it and the square
       edge (3px of border on a right-edge strip), and "Leaders" sat on it;
       the row tucks under no page, so the edge gets its own breathing room */
    padding-left: 8px;
  }
  /* "Leaderboard" is the longest door name, and at the title's 32px it needs
     216px of words in a banner a phone caps at 240 with 64 of tails, so it
     read "[Leaderboar". The close ribbon sits 3px past the banner's right end,
     so the banner cannot grow; the words shrink instead, for this door only. */
  body.skin-majesty .ws-sheet[data-door='board'] .ws-sheet-title {
    font-size: 24px;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   THE BAG, ALBION'S INVENTORY (Wray's pick B, 2026-09-06, kit section 9)
   ──────────────────────────────────────────────────────────────────────
   What is worn on top, a carry meter, then the grid; the type and rarity
   ribbons stay OUTSIDE the window as they are ("we can use the ribbons
   outside for sorting"). No value line: the inventory rows carry no value
   field, and a number with nothing behind it is the over-promise this repo
   guards against. No sort button: the ribbons are the sort.
   ══════════════════════════════════════════════════════════════════════ */
/* ⭐AN ANATOMICAL PAPER DOLL (#2020, Wray 2026-09-08, over the strip he
   picked on 2026-09-06). The slots sit AROUND A BODY, so position carries the
   meaning and no slot needs a word:

            head
     hand   body   pet
     trkA          trkB

   Which is what lets the two trinkets finally read as different slots -- as a
   strip they were both labelled TRK, because that is what `trinket_1` and
   `trinket_2` both abbreviate to.

   THE OLD FLEX ROW'S PHONE PROBLEM DOES NOT COME BACK: it wrapped because six
   48px cells need 270px against a 266px paper. Three columns need 156, so the
   doll fits where the row did not. */
body.skin-majesty .ww-worn {
  /* ⭐THE WORN SLOTS ARE BIGGER THAN A BAG CELL (Wray, 2026-09-08: "the
     paperdoll item slots look a bit lackluster").
     
     Measured on the reference: Albion's equipment slots run about 1.3x its
     inventory cells, and the ghost silhouette fills 55-60% of the slot. Ours
     were the SAME 48px as a grid cell with a 16px glyph -- 33% fill -- so the
     doll read as five more grid squares that happened to be empty.
     
     64 against the grid's 48 is 1.33x. `--sk-cell` is redeclared here rather
     than a new variable, because `.ww-slotc` already sizes itself from it and
     the whole point of the shared cell is that one number moves it. */
  /* 64, was 80: nine slots in a FULL 3x3 is 200px where six in a gappy one
     was 250, so more slots cost less height (#2020). */
  --sk-cell: 64px;
  display: grid;
  grid-template-columns: repeat(3, var(--sk-cell, 48px));
  /* the fourth row is the mount's, under the boots (#3031 PR 2): Albion's
     own doll, shoes under the armour with the potion and the food either
     side. Every cell is 64, so the boots cost one row, not a wider doll. */
  /* ⭐and the gathering tool's, under the potion (#3629). Its cell is drawn
     only on the ladder (world-windows.js syncToolCell); off it the area is
     as empty as the "." it replaced, so the doll draws exactly as before. */
  grid-template-areas:
    "trkA   head  trkB"
    "hand   body  pet"
    "potion feet  food"
    "tool   mount .";
  justify-content: center;
  gap: 4px;
  flex: 0 0 auto;
  margin-bottom: 2px;
}
/* the glyph needs a positioned parent, and the shared cell is a flex box
   rather than a relative one -- `.we-slot-k` is absolutely centred */
body.skin-majesty .ww-wornc { position: relative; }

/* ⭐THE GHOST DOUBLES, AND THE SHEET SCALES WITH IT. `.we-slot-k` is a 16px
   window onto a 96x32 sheet; at 32px the window and the sheet both double, or
   the glyph shows four icons at once. The X offset is written per-slot in JS
   at 1x, so it is scaled here by the same factor rather than in two places. */
body.skin-majesty .ww-worn .we-slot-k {
  width: 32px;
  height: 32px;
  margin: -16px 0 0 -16px;
  background-size: 192px 64px;
  background-position-y: -32px;      /* the GREY row, at 2x */
  transform: scale(1);
  opacity: .7;
}
/* the per-slot column offset is written at 1x by wornRow(); double it here so
   the two never have to agree on a number */
body.skin-majesty .ww-worn .we-slot-k { background-position-x: calc(var(--gx, 0px) * 2); }

/* ⭐AN EMPTY WORN SLOT IS NOT A SOCKET (measured on the reference): Albion
   draws its empty equipment slots as a FLAT tinted square with a big
   silhouette and NO stone frame, and saves the frame for slots that hold
   something. Ours wore the same dashed sprite either way, which is most of
   why the doll read flat -- six identical squares, five of them empty.
   
   So an empty worn slot sheds the sprite and becomes a faint recess; a filled
   one keeps it. Nothing changes in the bag grid, where every cell is a socket
   whether or not it holds a thing. */
/* ⛔A LOCKED SLOT LOOKS UNBUILT ON PURPOSE (#2020). Mount, food and potion are
   not item slots in this game; Wray asked for them as "a reminder what to
   build". Dimmer than empty and with no glyph, so it reads as a space kept
   rather than a control that answers nothing. */
body.skin-majesty .ww-wornc.is-locked {
  background-image: none;
  background-color: rgba(58, 36, 18, .06);
  box-shadow: inset 0 0 0 2px rgba(58, 36, 18, .08);
  opacity: 1;
  cursor: default;
}
body.skin-majesty .ww-wornc.is-empty {
  background-image: none;
  background-color: rgba(58, 36, 18, .10);
  box-shadow: inset 0 0 0 2px rgba(58, 36, 18, .13);
  opacity: 1;
}
body.skin-majesty .ww-wornc.is-empty { opacity: .8; }
/* `.ww-wornlab`, not `.ww-mark`: `.ww-mark` is the journal's vertical ribbon
   further up this file, and the first build of the worn row inherited an
   86px-tall ribbon into a 40px cell. One name, one component. */
body.skin-majesty .ww-wornlab {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  line-height: 1;
  white-space: nowrap;
  color: #8a7355;
  pointer-events: none;
}
body.skin-majesty .ww-carry {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  /* as wide as the grid it sits over */
  width: calc(var(--bc, 8) * var(--sk-cell, 40px) + (var(--bc, 8) - 1) * 3px);
  max-width: 100%;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #5a4328;
}
/* ⭐THE CARRY BAR IS THE XP BAR'S TROUGH (Wray, 2026-09-08: "carry capacity
   reuse the exp bar below or a different bar completely for carry capacity").
   
   `.ws-meter` is the pack's 20px segmented plate with a gold repeating fill.
   Beside a paper doll it read as another piece of ornament, and at 55/24 it
   was pinned full and saying nothing. The XP bar built for the player plate
   the same day (#2665) is a thin sunk trough with a flat fill -- quieter, and
   already the world's language for "how far along something is".
   
   ⛔THE COLOUR CARRIES THE ONE FACT THE NUMBER HAS TO SHOUT: over capacity.
   The XP bar is blue because XP is progress; carry turns AMBER as it fills and
   RED past the line, because "55 / 24" is a warning and a blue bar drawn at
   100% is not. `.is-over` is set by carryMeter from the same count the number
   comes from, so the colour can never disagree with the figure. */
body.skin-majesty .ww-carry .ws-meter {
  flex: 1 1 0;
  height: 10px;
  border: 2px solid #33240f;
  background: #3c2c18;
  border-radius: 0;
  overflow: hidden;
}
/* the trough has no drawn frame: the base rule's meter.png overlay (#2184)
   stays off here, and the fill may run the full width */
body.skin-majesty .ww-carry .ws-meter::after { content: none; }
body.skin-majesty .ww-carry .ws-meter > * {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  height: auto;
  max-width: none;
  background: repeating-linear-gradient(90deg, #c49a3e 0 12px, #8a6d2f 12px 16px);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .22), inset 0 -2px 0 rgba(0, 0, 0, .3);
  transition: width 200ms linear;
}
/* the warning band weight has and a socket count never did (#2832): amber
   from 80% of the cap, so the slowdown past it is seen coming. Same stripe,
   a warmer pair, between the gold and the red. */
body.skin-majesty .ww-carry.is-warn .ws-meter > * {
  background: repeating-linear-gradient(90deg, #d08a3a 0 12px, #935f26 12px 16px);
}
body.skin-majesty .ww-carry.is-warn b { color: #8a4f1c; }
body.skin-majesty .ww-carry.is-over .ws-meter > * {
  background: repeating-linear-gradient(90deg, #c9524b 0 12px, #8f3a35 12px 16px);
}
body.skin-majesty .ww-carry b { font-weight: 400; color: #2e2113; }
body.skin-majesty .ww-carry.is-over b { color: #9c3b34; }

/* ══════════════════════════════════════════════════════════════════════
   THE CHARACTER WINDOW, TWO COLUMNS (Wray's pick C, 2026-09-06)
   ──────────────────────────────────────────────────────────────────────
   "B looks best but C is the better choice if we have to move things to
   fill that space eventually so maybe C is the direction." Portrait and
   slots on the left, the sheet on the right, Albion's Appearance
   proportions. CSS only: world-character.js builds the same nodes in the
   same order; the grid places them. The slots wear the pack's inventory
   slot through `.ww-slotc` above, which is B's part of the pick.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .wc-window { width: min(600px, calc(var(--fw, 100vw) - 16px)); }
body.skin-majesty .wc-root {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 18px;
  row-gap: 10px;
  align-items: start;
}
/* the doll is the whole left column; everything else stacks on the right */
body.skin-majesty .wc-doll {
  grid-column: 1;
  grid-row: 1 / span 8;
  display: grid;
  grid-template-columns: auto;
  /* the portrait, then HEAD BODY WPN, then FAM TRK TRK: two rows of three */
  grid-template-areas: "mid" "l" "r";
  justify-items: center;
  align-items: start;
  gap: 8px;
  padding-top: 0;
}
body.skin-majesty .wc-root > :not(.wc-doll) { grid-column: 2; }
body.skin-majesty .wc-doll > :not(.wc-col) { grid-area: mid; }
body.skin-majesty .wc-doll > .wc-col:first-child { grid-area: l; }
body.skin-majesty .wc-doll > .wc-col:last-child { grid-area: r; }
/* each column of three becomes a row of three: two rows under the portrait */
body.skin-majesty .wc-col { flex-direction: row; gap: 6px; }
/* the portrait in the brass frame every button wears */
body.skin-majesty .wc-portrait {
  border: 0;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  border-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  background: var(--sk-hole);
  image-rendering: pixelated;
}
/* the stats read as a ruled table down the right: label, number, preview */
body.skin-majesty .wc-stats {
  display: grid;
  grid-template-columns: auto auto 1fr;
  column-gap: 12px;
  row-gap: 0;
  justify-content: start;
  padding: 0;
  border: 0;
}
body.skin-majesty .wc-stat { display: contents; }
body.skin-majesty .wc-stat > * {
  padding: 3px 0;
  border-bottom: 1px solid rgba(110, 80, 40, .34);
}
body.skin-majesty .wc-stat b {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #5a4328;
  align-self: center;
}
body.skin-majesty .wc-skills { justify-content: flex-start; }
body.skin-majesty .wc-build { justify-content: flex-start; }

/* ON A PHONE the window is the picture less 16, so the two columns stack:
   the doll on top, the sheet under it, as the old layout read. */
@media (max-width: 560px) {
  body.skin-majesty .wc-root { grid-template-columns: 1fr; }
  body.skin-majesty .wc-doll { grid-column: 1; grid-row: auto; }
  body.skin-majesty .wc-root > :not(.wc-doll) { grid-column: 1; }
}

/* ══════════════════════════════════════════════════════════════════════
   ⭐THE CHAT STRIP COLLAPSES TO A TAB (#2020, 2026-09-08)
   ──────────────────────────────────────────────────────────────────────
   Wray, measuring our HUD against Albion's: Albion spends one small tab on
   chat and takes the corner back. Ours spent a 460px input on it always, and
   to a guest that input says "connect X or a wallet to speak" -- so the
   widest thing bottom-left was an advert for a locked door.

   The JS adds one button and one class; all the hiding is here, so the input,
   the verbs and the Say button keep their identity, their listeners and the
   mute state that world-presence.js re-asserts last on purpose.

   `display: none` rather than visibility or width, because the strip is a
   flex row in the foot: a zero-width child still takes its gap, and the tab
   would sit adrift of the corner by exactly that much. */
body.skin-majesty #world-chat .wp-tab {
  display: none;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  height: 30px;
  padding: 0 12px;
  align-self: center;
  white-space: nowrap;
}
body.skin-majesty #world-chat.wp-collapsed .wp-tab { display: inline-flex; align-items: center; }
/* ⭐THE TAB IS THE SAME PLATE AS THE VERBS AND SAY (Wray, 2026-09-12: "the
   bottom left chat button ... its rendered in the old discontinued
   leather"). CHAT_CSS in world-presence.js dresses every `#world-chat
   button` in the leather gradient plus the `--wc-button` nine-slice; the
   verbs and Say were re-cut to `btn-idle.png` further up this file and this
   one button was missed, so the one control a player sees all day was the
   last leather in the corner. Same slice, same states. */
body.skin-majesty #world-chat .wp-tab {
  border-style: solid;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 10px;
  border-width: 10px;
  border-image-repeat: repeat;
  border-radius: 0;
  background: none;
  box-shadow: none;
  image-rendering: pixelated;
  color: var(--sk-ink);
  text-shadow: none;
}
body.skin-majesty #world-chat .wp-tab:hover {
  border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a');
  background: none;
  color: var(--sk-ink);
  filter: none;
}
body.skin-majesty #world-chat .wp-tab:active {
  border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a');
}
/* ⛔THE TAB IS NOT A MUTED CONTROL. CHAT_CSS dims every button in the strip to
   0.45 while the player may not speak, which is right for the verbs and for
   Say and wrong for this one: the tab does not send anything, it OPENS the
   panel, and a guest reading the room is exactly who needs to find it. Left
   alone it drew at 45% -- measured, a barely-legible plank on dark water. */
body.skin-majesty #world-chat.wp-muted .wp-tab { opacity: 1; cursor: pointer; }
body.skin-majesty #world-chat.wp-collapsed input,
body.skin-majesty #world-chat.wp-collapsed button[type='submit'],
body.skin-majesty #world-chat.wp-collapsed .wp-verb { display: none; }
/* COLLAPSED, THE STRIP IS THE TAB. It is `flex: 0 1 460px` in the foot for the
   open state; collapsed it must shrink to its one child or it holds the same
   460px of empty corner it was collapsed to release. */
/* ⛔EVERY PROPERTY THE OPEN STATE SETS HAS TO BE ANSWERED -- the exact lesson
   #world-chat's own placement comment records, hit again here. `flex` alone
   left `.wp-in-rail`'s `width: 100%` standing, so the collapsed strip measured
   1888px: a 62px tab with 1826px of invisible form beside it, holding the
   whole corner it was collapsed to release. Measured, not reasoned. */
body.skin-majesty #wt-foot #world-chat.wp-collapsed,
body.skin-majesty #world-chat.wp-collapsed {
  flex: 0 0 auto;
  width: auto;
  min-width: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   ⭐THE GRID SITS IN A WELL (#2020, Wray 2026-09-08)
   ──────────────────────────────────────────────────────────────────────
   Measured against the real Albion inventory: their cells are DARK stone
   sockets on a light parchment panel, so the grid reads as a field of holes.
   Ours is `slot-holder.png` -- a dark rim around a TAN interior -- on tan
   parchment, so cell and panel are the same value and the grid read flat.
   `.ww-slotc`'s own comment says the intent was "the cell becomes a hole in
   the panel rather than a tile on it"; it was not achieving that.

   ⛔THE CELL ART IS NOT THE THING TO CHANGE. It is Wray's 2026-09-06 pick
   from the Majesty pack ("it has inventory slot graphics that probably would
   suit us"), and he chose this fix over editing it: put the GRID in a
   recessed dark field, so the tan cells read as holes by contrast with their
   surround. The sprite is untouched.

   The well is the same navy the journal and the notices wear, at low alpha
   over the parchment rather than as its own opaque slab -- the panel is
   still the ground (#2588), this is a shadow cast into it. */
/* ⭐ONLY THE INVENTORY SCROLLS (Wray, 2026-09-08: "i dont like that i have to
   scroll inside the parchment it should all be there with the exception of
   scrolling down the inventory slightly maybe").

   ⛔THE SCROLL WAS ON THE WRAP, so at anything under a 1080-tall window the
   worn slots, the carry meter and the rarity ribbons scrolled away WITH the
   grid. Measured at 1512x831: 590px of content in a 541px box. At 1920x1080
   it does not overflow at all, which is exactly why the first pass missed it
   -- one viewport is not a measurement.

   So the wrap holds still and the GRID takes the remaining height and scrolls
   inside itself. Everything that is chrome stays put; the only thing that
   moves is the thing he said may move. */
body.skin-majesty .ww-bagwrap {
  /* so the well below can take the height the items do not */
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
body.skin-majesty .ww-worn,
body.skin-majesty .ww-bagwrap .ww-carry,
body.skin-majesty .ww-bagwrap .ww-rarerow,
body.skin-majesty .ww-bagwrap .ww-foot { flex: 0 0 auto; }
body.skin-majesty .ww-bag1 {
  /* ⭐THE WELL TAKES THE REST OF THE PANEL, AND TILES ITSELF FULL.
   *
   * The first cut padded the grid to a whole ROW of its own contents, which
   * is not what Albion does: theirs fills to the bottom EDGE. With the panel
   * now 81% tall (875px at 1080) that left 330px of blank parchment -- the
   * dead space this was meant to kill, made worse.
   *
   * ⛔PADDING IT IN JS WOULD MEAN MEASURING THE PANEL, which bagGrid cannot
   * do: it builds the tree before anything is mounted, so there is no height
   * to read. A resize would have to re-pad too. So the EMPTY sockets are the
   * well's own tiled background instead -- one sprite at the cell pitch, and
   * it is right at every panel height with no DOM and no listener.
   *
   * THE PITCH IS THE GRID'S: 48px cell + 3px gap = 51, offset by the 6px
   * padding, so the tiles land exactly where cells would and the 3px seams
   * line up with the real gaps.
   *
   * The gradient is a layer ON TOP of the tile and UNDER the content, which
   * is what dims the empty sockets without touching the real cells: a DOM
   * child always paints above every background layer of its parent. */
  flex: 1 1 auto;
  /* or the grid refuses to shrink below its own content and pushes the
     overflow back out onto the wrap -- the bug this block exists to fix */
  min-height: 0;
  overflow-y: auto;
  align-content: start;
  background-color: rgba(16, 24, 34, .30);
  background-image:
    linear-gradient(rgba(16, 24, 34, .60), rgba(16, 24, 34, .60)),
    url('/assets/ui/slot-holder.png?v=2026-09-06a');
  background-size: auto, calc(var(--sk-cell, 48px) + 3px) calc(var(--sk-cell, 48px) + 3px);
  background-repeat: repeat, repeat;
  background-position: 0 0, 6px 6px;
  /* ⛔`local`, NOT the default `scroll`. A scrolling box paints its background
     against its own border box, so the default holds the empty sockets still
     while the real cells slide over them: aligned on the first screen and
     nowhere after that. */
  background-attachment: local, local;
  image-rendering: pixelated;
  box-shadow:
    inset 0 3px 0 rgba(0, 0, 0, .34),
    inset 0 -2px 0 rgba(230, 200, 138, .10),
    inset 3px 0 0 rgba(0, 0, 0, .22),
    inset -2px 0 0 rgba(0, 0, 0, .22);
  padding: 6px;
  border-radius: 0;
}

/* ⛔THE GRID'S SCROLLBAR IS NOT A WHITE BAR (Wray, 2026-09-08: "i dont like
   the scroll bar inside the slots part, needs to not be a white bar").
   It is the platform's, and on his machine it draws a pale track straight down
   the parchment. Styled into the well's own colours, and narrow, because the
   bar is a position indicator here and not something anyone drags. */
body.skin-majesty .ww-bag1::-webkit-scrollbar { width: 8px; }
body.skin-majesty .ww-bag1::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, .28);
}
body.skin-majesty .ww-bag1::-webkit-scrollbar-thumb {
  background: #5a4328;
  border: 2px solid rgba(0, 0, 0, .35);
}
body.skin-majesty .ww-bag1::-webkit-scrollbar-thumb:hover { background: #8a6d2f; }
/* Firefox, which has no pseudo-elements for this */
body.skin-majesty .ww-bag1 { scrollbar-width: thin; scrollbar-color: #5a4328 rgba(0, 0, 0, .28); }

/* AN EMPTY SOCKET IS A SOCKET. Same sprite as a full cell, no rarity rim, and
   dimmed a little so a bag with room in it reads as room rather than as a
   grid of identical squares you have to count. It is a <span>, not a button:
   nothing to press, and dozens of focus stops before the next real control
   would be worse than the blank parchment this replaced. */
body.skin-majesty .ww-slotc.ww-empty {
  opacity: .42;
  cursor: default;
  box-shadow: none;
}
body.skin-majesty .ww-slotc.ww-empty:hover { border-image-source: none; }
/* past the carry line AND empty: dimmer still, so the line stays legible
   through the empty rows rather than only across the filled ones */
body.skin-majesty .ww-slotc.ww-empty.is-over { opacity: .2; }

/* AN ITEM WE COULD NOT DRAW (#2020). The same glyph an empty slot wears, from
   the COLOUR row rather than the grey one and at full strength -- grey means
   the slot is empty, colour means there is something in it we have no art
   for. It replaces two-letter initials, which read as a rendering failure
   eight cells at a time. */
body.skin-majesty .ww-noart {
  background-position-y: 0;
  opacity: .9;
}

/* ══════════════════════════════════════════════════════════════════════
   ⭐THE BAG'S HEADER AND FOOTER (#2020, Wray 2026-09-08)
   ──────────────────────────────────────────────────────────────────────
   Albion's inventory opens with a portrait, "mustbenice's" over "Inventory",
   and one headline number; it closes with a row of controls and a total. Ours
   opened with a ribbon reading "Bag" and closed with a sentence.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-baghead {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 0 2px 6px;
  border-bottom: 2px solid rgba(58, 36, 18, .18);
  margin-bottom: 6px;
}
/* the same brass ring the rail plate's portrait wears, so one cat has one
   frame wherever it shows */
body.skin-majesty .ww-bagbust {
  /* 64, was 44 (Wray, 2026-09-08: "I think the player profile circle on the
     bag page is too small"). Albion's is the tallest thing in its header and
     sets the header's height; at 44 ours was smaller than the two lines of
     text beside it. */
  flex: none;
  width: 64px;
  height: 64px;
  box-sizing: border-box;
  border: 3px solid #caa982;
  border-radius: 50%;
  background: #0e0c14;
  box-shadow: 0 3px 0 rgba(0, 0, 0, .5);
  image-rendering: pixelated;
}
body.skin-majesty .ww-bagheadtext { flex: 1 1 0; min-width: 0; }
body.skin-majesty .ww-bagowner {
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #5a4328;
  line-height: 1.5;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.skin-majesty .ww-bagtitle {
  /* ⛔`--sk-tome`, NOT `--sk-title`. There is no `--sk-title` in this file:
     the invented name fell through to a bare 'NotJamBlkltr16', which is not
     the @font-face family either (that is 'New Rocker'), so both
     headings rendered in the browser's serif inside a pixel-art window.
     `--sk-tome` is what every other blackletter heading here uses. */
  font-family: var(--sk-tome);
  font-size: 20px;
  line-height: 1.1;
  color: #3a2412;
}
/* the headline number, right, the way Albion sets its item power */
body.skin-majesty .ww-bagpower {
  flex: none;
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 11px;
  color: #7a5a2a;
}
body.skin-majesty .ww-bagpower:empty { display: none; }

/* ── the footer: two controls that work, and a count ──────────────────
   ⛔Albion's row is four buttons; ours is two, because stacking and a
   slot-size setting are features we do not have and a control that answers
   nothing is what the flags rule exists to prevent. */
body.skin-majesty .ww-footbar {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding-top: 6px;
  margin-top: 2px;
  border-top: 2px solid rgba(58, 36, 18, .18);
}
body.skin-majesty .ww-find {
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #3a2412;
  border-radius: 0;
  height: 34px;
  padding: 0 8px;
  outline: none;
  /* SUNK, not outlined: a hole in the parchment reads as somewhere to put
     something, which is the same call `#world-chat input` makes. */
  border: 0;
  background: rgba(58, 36, 18, .16);
  box-shadow: inset 2px 2px 0 rgba(58, 36, 18, .40),
              inset -1px -1px 0 rgba(255, 245, 220, .35);
}

/* ⭐THE SORT CONTROL IS DRAWN, NOT A BROWSER DROPDOWN (Wray, 2026-09-08:
   "sorting bar needs redrawing as you said").
   
   It was a bare <select>, which paints the platform's own widget -- a grey
   rounded box with a system chevron -- in the middle of a parchment window
   made of pixel art. `appearance: none` takes the widget off and leaves an
   element we can dress like every other plank in this file, with the pack's
   button nine-slice and its own drawn arrow.
   
   ⛔IT STAYS A <select>. The drawn-from-scratch alternative is a listbox with
   its own keyboard handling, focus trap and open/close state -- and the popup
   a native select gives free is the one part of it a page cannot draw over
   the top of a WebGL canvas without more machinery than the control is worth. */
body.skin-majesty .ww-sort {
  flex: none;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  /* ⛔34, NOT 26. The drawn frame costs 8px of border at the top and 10 at the
     bottom (4/5 slice at x2), so a 26px control had 8px of content box for an
     8px face plus its padding and clipped the descenders -- the identical
     failure this stylesheet already records for the tray's controls: "below a
     certain height the drawn frame is not an option". Measured, then sized to
     the border plus the line rather than guessed. */
  height: 34px;
  color: var(--sk-ink, #e6c88a);
  background-color: transparent;
  /* ⛔NO DRAWN ARROW, AND IT IS NOT AN OMISSION. A pixel chevron was built here
     as two gradients and never appeared: `border-image-slice: ... fill` paints
     the button's own middle OVER the element's background, so any
     background-image on a plank is invisible by construction. Verified by
     magnifying the rendered control, not by reading the rule.
     
     The alternatives are all worse than none: dropping `fill` loses the
     plank's interior, and a real chevron means wrapping the select in a
     positioned box so a pseudo-element can sit above it. The label already
     says what the control does. */
  padding: 1px 8px 3px 6px;
  image-rendering: pixelated;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  border-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
}
body.skin-majesty .ww-sort:hover { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
/* the popup itself is the platform's and cannot be skinned; at least give its
   rows the window's own ink rather than white-on-white */
body.skin-majesty .ww-sort option { color: #3a2412; background: #e8d2a8; }
body.skin-majesty .ww-find { flex: 1 1 0; min-width: 0; }
body.skin-majesty .ww-find::placeholder { color: #9d8a6d; }
body.skin-majesty .ww-find:focus,
body.skin-majesty .ww-sort:focus { border-color: var(--sk-gold, #c49a3e); }
body.skin-majesty .ww-count {
  flex: none;
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #5a4328;
  white-space: nowrap;
}

/* ⭐THE BAG'S OTHER DESTINATIONS, INSIDE THE PANEL (#2020). Albion puts
   "Loadouts" and "Wardrobe" here, under the paper doll: a small control each,
   not a ribbon hanging off the window's edge. Ours are Loadouts and Craft, and
   they are the last two of the eleven ribbons that used to sit outside. */
body.skin-majesty .ww-ways {
  display: flex;
  gap: 6px;
  justify-content: center;
  width: 100%;
  margin: 2px 0 4px;
}
body.skin-majesty .ww-way {
  /* ⛔`background: none`, OR THE UA'S BUTTONFACE SHOWS AROUND THE PLANK. Wray:
     "white space around the loadout/craft button" -- a <button> defaults to a
     light system fill, and the nine-slice's `fill` only covers the middle
     slice, so the default painted a pale rectangle just outside the drawn
     frame. Every other plank in this file sets this for the same reason. */
  background: none;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  color: var(--sk-ink, #e6c88a);
  cursor: pointer;
  padding: 1px 4px 3px;
  image-rendering: pixelated;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  border-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
}
/* btn-on / btn-press, the same pair every other plank in this file uses;
   there is no btn-hover.png and asking for one draws an empty frame */
body.skin-majesty .ww-way:hover { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ww-way:active { border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a'); }

/* ══════════════════════════════════════════════════════════════════════
   ⭐THE BAG'S COMPANION PANEL (#2020, Wray 2026-09-08)
   ──────────────────────────────────────────────────────────────────────
   "Loadout button should open a window onto the left side of the screen not
   overtake the bag screen."

   Albion opens Loadouts and Wardrobe as rails on the FAR LEFT while the
   inventory stays on the right, which is the placement rule #2557 measured:
   a narrow panel docks to a side, and a pair opens on opposite edges.

   It is `fixed`, so being a child of the bag's own body costs it nothing
   positionally -- and being that child is what makes its teardown free when
   `closeSheet` empties the body. It mirrors the sheet's own geometry off the
   same variables, so the two windows are the same height and the same
   distance from their edges without either number being typed twice.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-aside {
  position: fixed;
  /* ⛔IT COMPUTES ITS OWN SIZE AND DOES NOT INHERIT THE SHEET'S. `--sk-win-cw`
     and `--sk-win-ch` are declared ON `.ws-sheet`, and this panel hangs off
     <body>, so reading them here silently took the fallbacks -- measured 700px
     tall against the sheet's 950 on the first build.

     ⭐AND IT KEEPS THE TRAY RESERVED, because unlike the bag this panel IS on
     the left, which is where Nerm's speech bubble sits. So it is deliberately
     ~50px shorter than the window it partners: the pair are not the same
     height, and the reason is that one of them has something underneath it. */
  /* ⭐BESIDE THE BAG, NOT ON THE FAR EDGE (Wray, 2026-09-12: "that build
     button on the bag window can open a smaller panel left of the bag
     window in parchment, where players can save 2-3 builds, so you carry
     those builds with you"). toggleAside measures the open sheet and sets
     left/top/height inline off that rectangle, so the pair share an edge
     and a height without either number typed twice; these are the no-JS
     values only. The tray is not subtracted any more: the panel is on the
     bag's side of the screen now, and Nerm is not under it. */
  --ww-aside-band: calc(var(--fh, 100vh) - var(--sk-foot, 38px));
  left: calc(var(--fx, 0px) + 8px);
  top: calc(var(--fy, 0px) + 96px);
  /* the WIDTH is set inline by toggleAside, measured off the sheet. Every
     other option duplicated the bag's own floor and share in a second place,
     which is the drift this file keeps paying for; this way the pair cannot
     disagree because there is only one number. The declaration below is the
     floor for the moment before that runs. */
  width: 320px;
  height: calc(var(--ww-aside-band) - 96px - 8px);
  box-sizing: border-box;
  z-index: 41;
  display: flex;
  flex-direction: column;
  /* the sheet's own parchment, drawn the same way -- `round` rather than
     `stretch` for the reason the sheet carries: a stretched slice bands */
  background-color: #d5b68c;   /* the border's inner edge, as the sheet */
  background-clip: padding-box;   /* same reason as the sheet: no straight edge behind the torn one */
  border-style: solid;
  border-image-source: url('/assets/ui/paper.png?v=2026-08-29a');
  border-image-slice: 28;
  border-image-width: 28px;
  border-width: 28px;
  border-image-repeat: round;
  image-rendering: pixelated;
  color: #2e2113;
  box-shadow: 0 8px 0 rgba(0, 0, 0, .45);
}
body.skin-majesty .ww-asidehead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex: 0 0 auto;
  padding-bottom: 6px;
  border-bottom: 2px solid rgba(58, 36, 18, .18);
  margin-bottom: 6px;
}
body.skin-majesty .ww-asidetitle {
  /* ⛔`--sk-tome`, NOT `--sk-title`. There is no `--sk-title` in this file:
     the invented name fell through to a bare 'NotJamBlkltr16', which is not
     the @font-face family either (that is 'New Rocker'), so both
     headings rendered in the browser's serif inside a pixel-art window.
     `--sk-tome` is what every other blackletter heading here uses. */
  font-family: var(--sk-tome);
  font-size: 20px;
  line-height: 1.1;
  color: #3a2412;
}
body.skin-majesty .ww-asidex {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 12px;
  line-height: 1;
  color: #5a4328;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 2px 4px;
}
body.skin-majesty .ww-asidex:hover { color: #2e2113; }
body.skin-majesty .ww-asidebody { flex: 1 1 auto; min-height: 0; display: flex; }
/* the framed page paints its own ground; the window is the parchment (#2588) */
body.skin-majesty .ww-asideframe {
  flex: 1 1 auto;
  width: 100%;
  border: 0;
  background: transparent;
}

/* ── DRAG AFFORDANCES (#2020, Wray 2026-09-08: "Cant drag and drop items into
   the empty slots above, right clicking works however") ──────────────
   The slot that would take the item lights while the drag is live, and the
   one under the cursor lights harder. Both are classes set by the drag
   handlers, so the state is the same fact the drop acts on. */
body.skin-majesty .ww-slotc.is-dragging { opacity: .45; }
body.skin-majesty .ww-wornc.is-target {
  box-shadow: inset 0 0 0 2px var(--sk-gold, #c49a3e);
}
/* ⛔`.is-droptarget`, NOT `.is-over` (#3031 P0-1). `.is-over` is the carry
   line's dim on every `.ww-slotc` (opacity .45), and the doll's cells are
   `.ww-slotc` too, so the class meant to LIGHT the slot under a drag dimmed a
   filled one instead. The drag engine (world-equip.js) sets this one only. */
body.skin-majesty .ww-wornc.is-droptarget {
  opacity: 1;
  box-shadow: inset 0 0 0 3px var(--sk-gold-hi, #e6c07a);
  filter: brightness(1.12);
}
/* a piece dragged off the doll lights the grid it will go back into */
body.skin-majesty .ww-bag1.is-droptarget {
  box-shadow: inset 0 0 0 3px var(--sk-gold-hi, #e6c07a);
}
/* ⭐the bag is arranged by hand (#3031 PR 3): the cell a pile would move
   into lights, filled (a swap) or empty (a move), and so does the Bank
   panel when a pile would go into the bank */
body.skin-majesty .ww-bag1 .ww-slotc.is-droptarget {
  opacity: 1;
  box-shadow: inset 0 0 0 3px var(--sk-gold-hi, #e6c07a);
  filter: brightness(1.12);
}
/* ⛔a literal, not var(--sk-gold-hi): the panel hangs off <body>, where that
   variable resolves to the parchment tan (measured rgb(202,169,130) on the
   served page), and the ring disappeared into the paper */
body.skin-majesty .ww-aside.is-droptarget {
  box-shadow: 0 0 0 3px #e6c07a, 0 8px 0 rgba(0, 0, 0, .45);
}

/* ⭐A FINGER CAN SCROLL THE GRID AND STILL DRAG FROM IT (#3031 P0-1).
   `pan-y`, so a flick that starts on an item scrolls the grid natively, with
   its momentum; the drag engine starts a drag only after a finger RESTS on a
   cell (TOUCH_HOLD_MS) and then cancels the scroll itself. `none` would have
   stopped the flick dead on every cell, which is most of the grid. The
   callout and selection go, or iOS answers the hold with its own menu. */
body.skin-majesty .ww-bag1 .ww-slotc[data-item-id],
body.skin-majesty .ww-bag1 .ww-slotc[data-mat-key],
body.skin-majesty .ww-wornc[data-worn-slot],
body.skin-majesty .ww-cell[data-card-id],
body.skin-majesty .we-build[data-card-id] {
  touch-action: pan-y;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
body.skin-majesty .ww-slotc img { -webkit-user-drag: none; }

/* ── THE PINNED CARD (#3031 P0-2) ─────────────────────────────────────
   A tap holds the hover card open with one button on it: Equip on a bag
   cell, Take off on a filled doll cell. The hover card never takes the
   pointer (a tooltip that can be hovered flickers); the pinned one has to,
   or its button cannot be pressed. */
body.skin-majesty .ww-hover.is-pinned { pointer-events: auto; }
body.skin-majesty .ww-hover .ww-hover-act {
  display: block;
  margin: 8px auto 0;
  background: none;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 9px;
  letter-spacing: .04em;
  color: var(--sk-ink, #e6c88a);
  cursor: pointer;
  min-height: 32px;
  padding: 2px 10px 4px;
  image-rendering: pixelated;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  border-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
}
body.skin-majesty .ww-hover .ww-hover-act:hover { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ww-hover .ww-hover-act:active { border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a'); }

/* ── THE BUILD ON THE DOLL (#3031 PR 2, Wray's Q1 2026-09-23) ──────────
   A slot with nothing worn is the STARTER piece (L-B18): drawn as a ghost,
   because it is not an item and never goes in the bag, but it is still
   what fights on that button. Dimmed art and a dashed inset say "yours,
   not a thing you carry". */
body.skin-majesty .ww-wornc.is-virtual {
  opacity: 1;
  outline: 1px dashed rgba(90, 67, 40, .55);
  outline-offset: -5px;
  cursor: pointer;
}
body.skin-majesty .ww-wornc.is-virtual img {
  opacity: .5;
  filter: grayscale(.55) sepia(.25);
}
/* the boots have no glyph in slot-icons.png yet: the word, in the doll's
   own ghost ink, where the others draw their silhouette */
body.skin-majesty .ww-wornword {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  line-height: 1;
  letter-spacing: .04em;
  color: #8a7355;
  pointer-events: none;
}
/* THE SOCKET MARK: the card-face template's own socket square (x200 y14,
   28 of 240 wide) scaled to the cell, in its top-right corner. Hollow while
   the base fires; the socketed card's painted subject once one is in. */
body.skin-majesty .ww-sockmark {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 18px;
  height: 18px;
  box-sizing: border-box;
  border: 2px solid #5a4328;
  background: rgba(20, 14, 8, .55);
  box-shadow: 0 1px 0 rgba(0, 0, 0, .45);
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 2;
}
body.skin-majesty .ww-sockmark.is-full {
  border-color: var(--sk-gold, #c49a3e);
  box-shadow: 0 0 0 1px #2e2113, 0 1px 0 rgba(0, 0, 0, .45);
}
/* the piece's card: its abilities, named, under what it is */
body.skin-majesty .ww-hover .ww-piecebase {
  margin-top: 6px;
  padding-top: 5px;
  border-top: 1px solid rgba(46, 33, 19, .28);
}
body.skin-majesty .ww-hover .ww-piecebase p { margin: 0 0 2px; font-size: 16px; color: #2e2113; }
body.skin-majesty .ww-hover .ww-piecehint { display: block; font-style: normal; font-size: 15px; color: #8a7355; }
/* THE CARD STAYS WITH THE PIECE: said under the doll after a swap, with the
   one tap that moves it (L-B9, L-B17). Parchment ink, like the bag's note. */
body.skin-majesty .ww-socknotes { flex: 0 0 auto; width: 100%; }
body.skin-majesty .ww-socknote {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 4px 10px;
  margin: 2px 0 6px;
  font-family: 'VT323', ui-monospace, monospace;
  font-size: 17px;
  line-height: 1.2;
  color: #2e2113;
  text-align: center;
}
body.skin-majesty .ww-socknote p { margin: 0; }
/* its own line height: the line's 1.2 is sized for 17px VT323, and on the
   8px Press Start 2P plank a wrapped piece name ("Put it in your Plaguemire
   Hood") drew its second line over the first (#3031 review) */
body.skin-majesty .ww-socknote .ww-socknote-go { min-height: 32px; line-height: 1.7; }

/* ── WHERE THE GEAR IS, WHEN THE BAG IS EMPTY (#3031 P0-3) ───────────
   Above the grid, on the parchment, in the parchment's own ink: the line
   used to sit inside the well over the socket art and could not be read. */
body.skin-majesty .ww-bagnote {
  flex: 0 0 auto;
  width: 100%;
  text-align: center;
  font-family: 'VT323', ui-monospace, monospace;
  font-size: 17px;
  line-height: 1.2;
  color: #2e2113;
}
body.skin-majesty .ww-bagnote p { margin: 0 0 4px; }
body.skin-majesty .ww-bagnote .ww-bagnote-go { margin: 2px auto 4px; }

/* ── THE COMPANION PANEL, WHEN THERE IS NO ROOM BESIDE THE BAG (#3031 P0-4)
   On a phone toggleAside lays it over the bag (same rectangle) instead of at
   x=-320, so it has to sit one layer above the sheet (world-shell.css 60). */
body.skin-majesty .ww-aside.is-over-sheet { z-index: 61; }

/* ── THE BAG ON A NARROW SHEET (#3031 P0-4) ──────────────────────────
   The doll row kept two columns on a 274px phone sheet: the stats column got
   64px, the doll was drawn over the Build, Bank and Skills buttons, and the
   weight read "26 /" over "100". Below 380px of sheet body the doll goes on
   top and the stats, the buttons and the weight run full width under it.
   Keyed off the sheet body (the `ws-body` container), not the screen. */
@container ws-body (max-width: 380px) {
  body.skin-majesty .ww-dollrow {
    flex-direction: column-reverse;
    align-items: stretch;
  }
  body.skin-majesty .ww-dollrow .ww-worn { align-self: center; }
  body.skin-majesty .ww-dollcol { flex: 0 0 auto; width: 100%; }
  body.skin-majesty .ww-dollcol .ww-ways { justify-content: center; flex-wrap: wrap; }
  /* two columns of stats, not five rows: the doll already spends 200px */
  body.skin-majesty .ww-dollcol .ww-stats { grid-template-columns: 1fr 1fr; column-gap: 12px; }
  /* ⭐ONE SCROLL, THE WHOLE BAG. Stacked, the doll, stats, buttons and
     weight leave the grid ~110px: one row of three, scrolling inside a
     window that also scrolls is two scrolls a thumb has to tell apart. On a
     narrow sheet the grid is as tall as its rows and the bag scrolls as one
     page, the way a phone reads. */
  body.skin-majesty .ww-bagwrap { overflow-y: auto; }
  body.skin-majesty .ww-bag1 { flex: 0 0 auto; overflow: visible; }
}

/* ── THE STATS, ON THE BAG PAGE (#2020, Wray 2026-09-08) ──────────────
   "if we can fit the characters stats on the page without the pullout i think
   we should. then the character page becomes kind of pointless."

   Two columns rather than five stacked rows, because the doll above already
   spends 248px of height and the grid below is the thing that must not shrink.
   Label left, figure right -- the readout shape Albion uses for a stat and the
   one the player plate already uses for GP. */
/* ⭐THE DOLL AND THE STATS SHARE A ROW (Wray, 2026-09-08). The doll is three
   80px columns -- 248px in a body 412 wide -- so 150px of that row was empty
   while the stats spent their own height underneath. Side by side they cost
   the grid nothing, and the grid is the thing he wants back. */
body.skin-majesty .ww-dollrow {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 10px;
  width: 100%;
  flex: 0 0 auto;
}
/* ⭐THE BUILD BUTTON AND THE CARRY BAR MOVE INTO THIS COLUMN (Wray,
   2026-09-08: "carry capacity and build button can go up and to the side to
   give us more bag slots").
   
   The doll is 200px tall and the five stat rows are ~113, so the left column
   had ~87px of nothing under them while the button and the bar each spent a
   full row of the window's height below. Moved up beside the doll they cost
   the grid nothing, which is the same trade the stats made. */
body.skin-majesty .ww-dollcol {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1 1 0;
  min-width: 0;
}
body.skin-majesty .ww-dollcol .ww-ways { margin: 0; justify-content: flex-start; }
body.skin-majesty .ww-dollcol .ww-carry { width: 100%; }
/* ONE column now it is beside something, not two under it */
body.skin-majesty .ww-stats {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3px;
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
}
body.skin-majesty .ww-stat {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  color: #5a4328;
}
body.skin-majesty .ww-statk { flex: 0 0 auto; }
/* the figure takes the middle so the numbers line up down each column rather
   than floating after labels of different lengths */
body.skin-majesty .ww-statv { flex: 1 1 0; text-align: right; font-weight: 400; color: #2e2113; }
/* what the GEAR gives, which is the half a stat list is actually read for */
body.skin-majesty .ww-statb { flex: 0 0 auto; font-style: normal; min-width: 22px; }
body.skin-majesty .ww-stat.is-up .ww-statb { color: #4e7c3a; }
body.skin-majesty .ww-stat.is-down .ww-statb { color: #9c3b34; }

/* the word, for anything not looking at pictures */
body.skin-majesty .ww-sr {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── THE STATS ARE READABLE (#2020, Wray 2026-09-08) ──────────────────
   "The stats are very small, i feel like if you took more care making better
   use of the space the page would look very beautiful and be finished."
   7px was the size of a secondary chip, and these are the numbers the page is
   about. 10px for the figure, 8px for the label, and the row gets air. */
body.skin-majesty .ww-stats { gap: 6px; }
body.skin-majesty .ww-stat { font-size: 8px; gap: 8px; }
body.skin-majesty .ww-statv { font-size: 10px; }
body.skin-majesty .ww-statb { font-size: 8px; min-width: 26px; }

/* ══════════════════════════════════════════════════════════════════════
   ⭐A FILLED SLOT GETS THE WELL, AND ITS RARITY AS A RIM (#2020)
   ──────────────────────────────────────────────────────────────────────
   Wray picked from five mocked options (audit/chrome/2026-09-09-slot-mock):
   "i think A looks the best for items, but the sprites are too big for the
   box... E is actually the best from there. IF we could have A with a rarity
   border that might look better."

   So: A's ground (panel-well, the textured navy the journal wears) with E's
   rarity rim. The empty cell keeps the red dashes he likes.

   ⛔THE ICON DOES NOT SHRINK TO FIT -- "making the icons smaller is not the
   direction they are already at the max". So the WELL grows instead: it is
   the whole cell rather than an inset, and the rim rides its edge. That is
   why A looked cramped in the mock, and it was the mock's fault rather than
   the option's. */
/* ⭐OPTION E, NOT A (Wray, 2026-09-08 second look: "i changed my mind about
   the slots they dont look right, i think revert back to this look on
   screenshot 3 but lets try option E now instead").
   
   ⛔THE DIFFERENCE IS WHETHER THE DASHES SURVIVE. A replaced the whole cell
   with panel-well, so the red-dashed sprite he likes disappeared the moment a
   slot held anything -- which is why the grid stopped looking like his
   screenshot 3. E keeps the sprite and puts the dark panel INSIDE it, so the
   dashed frame reads on every cell and only the middle changes.
   
   A flat colour rather than panel-well's texture, which is the other half of
   E: at 54px the texture was noise behind an icon. */
body.skin-majesty .ww-bag1 .ww-slotc.is-full::before {
  content: '';
  position: absolute;
  /* ⛔INSIDE THE DASHES, NOT OVER THEM. slot-holder.png draws its red dashes at
     rows 4..35 of 40, which on a 64px cell is 6.4..56 -- so a 5px inset put
     the dark panel straight over them and the filled cells lost the dashed
     frame entirely. That is the half of "revert back to this look on
     screenshot 3" that matters: the dashes read on EVERY cell, and only the
     middle changes when something is in it. 9px clears them. */
  inset: 9px;
  background: #1b2430;
  /* the rarity, on the rim, where this grid has always carried it */
  box-shadow: inset 0 0 0 2px var(--r, transparent);
}
/* the sprite needs a positioned parent for the panel above, and the icon has
   to paint OVER it */
body.skin-majesty .ww-bag1 .ww-slotc { position: relative; }
body.skin-majesty .ww-bag1 .ww-slotc img { position: relative; z-index: 2; }
body.skin-majesty .ww-bag1 .ww-slotc.is-full img { width: 60%; height: 60%; }

/* ── THE CLOSE RIBBON RIDES THE TOP (#2020, Wray 2026-09-08) ──────────
   "the X ribbon just needs bumping up slightly youre right it is in line but
   it needs to look like its coming over the top so the pixel that sticks out
   to the left of that ribbon is what sits on top of the parchment."

   Its top edge measured exactly on the sheet's, which is what "in line" means
   and is not what a ribbon pinned OVER a page looks like. ribbon-closed.png
   carries a notch on its left side 4 art-pixels down; at x2 that is 8px, so
   lifting it 8 puts that notch ON the parchment's top edge and the ribbon's
   own top above it. */
/* ⛔-24, NOT -28. 8px put the notch on the parchment's edge by the art's own
   measurement and Wray read it as overshot -- "slightly overshot the edge for
   the X ribbon". Half of it: the notch sits ON the edge rather than above it. */
body.skin-majesty .ws-sheet-top .wr-plank { translate: 12px -24px; }

/* ── THE PORTRAIT IS BIGGER, AND MAY LEAN ON THE RULE BELOW IT ────────
   "Perhaps a slight larger player profile circle, its ok if it overlaps into
   the frame below." 64 -> 84, and the header's bottom rule is allowed to pass
   behind it rather than the header growing to clear it. */
body.skin-majesty .ww-bagbust { width: 84px; height: 84px; margin-bottom: -12px; }
body.skin-majesty .ww-baghead { align-items: flex-end; padding-bottom: 8px; }

/* ── THE STATS ARE STYLISED, NOT JUST BIGGER (#2020) ──────────────────
   "the stats showing there are still small, nothing there is stylised."
   They were two anonymous columns of 8px text. Now: the label in the data
   face at 8, the figure in the TOME at 15 -- the same face the window's own
   title uses -- on a ruled row, so the numbers read as the page's own
   typography rather than as a caption. */
body.skin-majesty .ww-stats { gap: 0; }
body.skin-majesty .ww-stat {
  align-items: baseline;
  gap: 8px;
  padding: 3px 2px;
  border-bottom: 2px solid rgba(58, 36, 18, .12);
}
body.skin-majesty .ww-stat:last-child { border-bottom: 0; }
body.skin-majesty .ww-statk {
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 8px;
  color: #7a6242;
  letter-spacing: .06em;
}
body.skin-majesty .ww-statv {
  font-family: var(--sk-tome);
  font-size: 15px;
  line-height: 1;
  color: #2e2113;
}
body.skin-majesty .ww-statb {
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 8px;
  min-width: 26px;
}

/* ── THE IN-PANEL TAB ROW (#2020, Wray 2026-09-09) ────────────────────
   "Albions store has 3 buttons at the top, purchasing premium etc."
   Across the top of the window, inside it, rather than on ribbons hanging off
   its edge. Drawn planks, the same nine-slice every button here wears; the
   current one is lit rather than outlined, because an outline round a plank
   reads as a second frame. */
/* ⛔AND IT WRAPS (#3031 2H, 2026-09-24). Measured at 390: the Store's four
   planks need 362px and its row has 258, so $9LV hung past the window's
   right edge with no way to reach it. A second row costs 40px; a hidden
   tab costs the tab. */
body.skin-majesty .ws-panelnav {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex: 0 0 auto;
  padding: 0 0 8px;
  margin-bottom: 8px;
  border-bottom: 2px solid rgba(58, 36, 18, .18);
}
body.skin-majesty .ws-paneltab {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  color: var(--sk-ink, #e6c88a);
  cursor: pointer;
  padding: 1px 8px 3px;
  image-rendering: pixelated;
  background: none;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  border-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  min-height: 34px;
}
body.skin-majesty .ws-paneltab:hover { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ws-paneltab:active { border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a'); }
body.skin-majesty .ws-paneltab[aria-current='page'] {
  border-image-source: url('/assets/ui/btn-bright.png?v=2026-08-29a');
  color: #fff3d6;
}
/* the page a station opened, named in its row (#3031 PR 4): a label, not a
   button, so it does not answer the pointer like one */
body.skin-majesty span.ws-paneltab { cursor: default; }
body.skin-majesty span.ws-paneltab:hover,
body.skin-majesty span.ws-paneltab:active { border-image-source: url('/assets/ui/btn-bright.png?v=2026-08-29a'); }
/* the frame takes what the row leaves, and nothing pushes past the window */
body.skin-majesty .ws-panelbody {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
body.skin-majesty .ws-sheet-body:has(.ws-panelnav) { display: flex; flex-direction: column; }

/* ══════════════════════════════════════════════════════════════════════
   ⭐THE LEADERBOARD'S TWO CONTROLS (#2020, Wray 2026-09-09)
   ──────────────────────────────────────────────────────────────────────
   "sort with the drop down what leaderboard you want... 4 of our leaderboards
   are different stacks, and 3 just change the time frame."

   A select for WHICH LADDER and a segmented row for WHICH PERIOD, the way
   Albion's Rankings does it — one axis where only one of four can be true, one
   where three short options are compared against each other and read better
   all visible.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-boardbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  flex-wrap: wrap;
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 2px solid rgba(58, 36, 18, .18);
}
body.skin-majesty .ww-boardseg { display: flex; gap: 4px; }
body.skin-majesty .ww-boardscope {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  color: var(--sk-ink, #e6c88a);
  cursor: pointer;
  padding: 1px 6px 3px;
  min-height: 34px;
  background: none;
  image-rendering: pixelated;
  border-style: solid;
  border-image-repeat: repeat;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 4 5 5 5 fill;
  border-image-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
  border-width: calc(4px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2)) calc(5px * var(--sk-btn-k, 2));
}
body.skin-majesty .ww-boardscope:hover { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ww-boardscope[aria-current='page'] {
  border-image-source: url('/assets/ui/btn-bright.png?v=2026-08-29a');
  color: #fff3d6;
}
/* the list takes what the bar leaves, and scrolls inside itself */
body.skin-majesty .ww-boardcontent { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
body.skin-majesty .ws-sheet[data-door='board'] .ws-sheet-body { display: flex; flex-direction: column; }

/* ── THE LADDER'S REMAINING ASKS (#2020, Wray 2026-09-09) ─────────────

   ⭐WIDER ROWS. "i think that the left/right margins can be smaller to get a
   wider bar on the page." The row was 8px in from each side inside a body
   that already has the sheet's 28px border; 2px gives the score column back
   the room it was losing to chrome. */
body.skin-majesty .ww-rank { padding: 5px 2px; }

/* ⭐WHICH WAY THEY MOVED. Drawn only when it moved: a dash on every unchanged
   row is fifty-seven marks saying nothing, and the two that matter stop being
   visible. Green up, red down, the one convention nobody has to be taught. */
body.skin-majesty .ww-move {
  font-family: var(--sk-data, 'Press Start 2P', monospace);
  font-size: 8px;
  line-height: 1;
  justify-self: end;
  white-space: nowrap;
}
body.skin-majesty .ww-move.is-up { color: #4e7c3a; }
body.skin-majesty .ww-move.is-down { color: #9c3b34; }
/* the arrow takes its own column so the score stays aligned down the ladder */
body.skin-majesty .ww-rank { grid-template-columns: 40px 1fr auto auto 72px; }

/* ⭐A ROW IS A DOOR TO A PROFILE. It frames /p/<handle> in the sheet rather
   than navigating, because leaving the page reloads the world and respawns
   you at the gate. */
body.skin-majesty .ww-rank.is-link { cursor: pointer; }
body.skin-majesty .ww-rank.is-link:hover { background: rgba(58, 36, 18, .10); }
/* ⛔THE HOVER WASH IS NOT A BACKGROUND FOR EVERY ROW (#3031, 2J review).
   The rule above is (0,4,1), and it replaced two things that are not a
   plain row's: your own line's OPAQUE fill, so hovering it while it sat
   stuck over another row printed that row through it (measured: "#28" read
   through "#41"), and the podium's plate, which it wiped to nothing. Each
   keeps its own ground and takes the same brown wash on top. */
body.skin-majesty .ww-rank.is-you.is-link:hover {
  background:
    linear-gradient(rgba(58, 36, 18, .10), rgba(58, 36, 18, .10)),
    linear-gradient(rgba(164, 52, 58, .12), rgba(164, 52, 58, .12)),
    #d5b68c;
}
body.skin-majesty .ww-rank.is-top.is-link:hover {
  background-image:
    linear-gradient(rgba(58, 36, 18, .10), rgba(58, 36, 18, .10)),
    repeating-linear-gradient(160deg,
      rgba(255, 255, 255, .10) 0 1px,
      rgba(0, 0, 0, 0) 1px 3px,
      rgba(0, 0, 0, .10) 3px 4px,
      rgba(0, 0, 0, 0) 4px 8px),
    linear-gradient(180deg, var(--pl-hi), var(--pl-mid) 55%, var(--pl-lo));
}
body.skin-majesty .ww-rank.is-link:focus-visible {
  outline: 2px solid var(--sk-gold, #c49a3e);
  outline-offset: -2px;
}

/* ⛔AND THE SCROLLBAR IS NOT THE PLATFORM'S WHITE ONE, same as the bag's
   (#2682). The list is the only thing that scrolls here. */
body.skin-majesty .ww-boardcontent::-webkit-scrollbar { width: 8px; }
body.skin-majesty .ww-boardcontent::-webkit-scrollbar-track { background: rgba(58, 36, 18, .16); }
body.skin-majesty .ww-boardcontent::-webkit-scrollbar-thumb {
  background: #8a6d2f;
  border: 2px solid rgba(58, 36, 18, .30);
}
body.skin-majesty .ww-boardcontent::-webkit-scrollbar-thumb:hover { background: #c49a3e; }
body.skin-majesty .ww-boardcontent { scrollbar-width: thin; scrollbar-color: #8a6d2f rgba(58,36,18,.16); }

/* ══════════════════════════════════════════════════════════════════════
   THE PLAYER PROFILE, ITS OWN WINDOW BESIDE THE LADDER (#2020)

   Wray, 2026-09-09, with audit/chrome/albion-ref/player-profile.png: "thats
   what happens when you click a player from the leaderboard, its own pop up
   profile, with tabs Details / Kills / Deaths / Achievements. build it out
   like this rather than inside that left leaderboard window."

   ⭐IT IS PARCHMENT, NOT THE DARK SURFACE. Measured off that shot: the profile
   wears exactly the material the Rankings window beside it wears. #2557's rule
   is that a docked panel is parchment and a WIDE CENTRED browser is dark, and
   this is a second docked panel, not a browser -- so the dark treatment would
   be the wrong half of the rule, however tempting "another player's data is
   the world's data" sounds.

   It borrows the aside's frame numbers rather than restating them, for the
   reason that file keeps paying for: two copies of one number drift.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-profile {
  position: fixed;
  /* ⛔ABOVE THE SHEET'S 60, and its strip at 61 between them. The two windows
     never overlap on a desktop, but on a phone the profile lands ON the ladder
     (see `place` in world-windows.js) and a 41 like the bag's companion panel
     would put it silently underneath. */
  z-index: 62;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  /* the floor for the frame before `place()` measures the ladder */
  width: 420px;
  height: 60vh;
  /* the sheet's own parchment, drawn the same way -- `round` rather than
     `stretch` because a stretched slice bands, and the background colour is
     the border's inner edge so no straight edge shows behind the torn one */
  background-color: #d5b68c;
  background-clip: padding-box;
  border-style: solid;
  border-image-source: url('/assets/ui/paper.png?v=2026-08-29a');
  border-image-slice: 28;
  border-image-width: 28px;
  border-width: 28px;
  border-image-repeat: round;
  image-rendering: pixelated;
  color: #2e2113;
  box-shadow: 0 8px 0 rgba(0, 0, 0, .45);
}

/* the ribbons tuck UNDER the parchment, which is why they are a sibling on
   <body> and not a child: a child cannot paint behind its parent's background,
   and `z-index: -1` lands between the background and the content rather than
   behind both. 61 against the card's 62 is the whole trick, the same one the
   sheet's own strips play at 59 against 60. */
body.skin-majesty .ws-tabs.ww-proftabs { z-index: 61; }

/* ── the head band: portrait, name, house and guild ──
   Albion's is a ringed round bust with the name in display type beside it and
   the affiliation under. Ours has the same three things and the same order. */
body.skin-majesty .ww-profhead {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  padding-bottom: 10px;
  border-bottom: 2px solid rgba(58, 36, 18, .18);
  margin-bottom: 10px;
}
body.skin-majesty .ww-profring {
  width: 64px;
  height: 64px;
  flex: 0 0 auto;
  border-radius: 50%;
  overflow: hidden;
  background: #c9a877;
  /* the gold rim the ladder's podium plates and the rail's plate both wear */
  box-shadow: 0 0 0 3px #a8813f, 0 0 0 5px rgba(58, 36, 18, .35);
}
body.skin-majesty .ww-profbust {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  image-rendering: pixelated;
}
body.skin-majesty .ww-proftext { flex: 1 1 auto; min-width: 0; }
body.skin-majesty .ww-profname {
  /* ⛔`--sk-tome`, NOT `--sk-title`. There is no `--sk-title` in this file, and
     an invented custom property does not throw -- it falls through to whatever
     is after the comma, which is how two headings came to render in the
     browser's serif inside a pixel-art window. */
  font-family: var(--sk-tome);
  font-size: 24px;
  line-height: 1.05;
  color: #3a2412;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.skin-majesty .ww-profsub {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 9px;
  letter-spacing: .06em;
  color: #5a4328;
  margin-top: 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ⛔BIG ENOUGH TO FIND. At the companion panel's 12px/#5a4328 this was a pale
   smudge in the corner of a 400px window -- measured on the shot, and the only
   way out of the profile besides closing the ladder underneath it. Albion's is
   a full round gold button; ours is the same × the panels use, at the ink the
   rest of the window is written in and a size a cursor can land on. */
body.skin-majesty .ww-profx {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 14px;
  line-height: 1;
  color: #6b2226;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 4px 6px;
  align-self: flex-start;
}
body.skin-majesty .ww-profx:hover { color: #2e2113; }

/* ── the inset the pages paint into ──
   Albion sets the Details list in a recessed panel a shade paler than the
   window, with the heading ruled off above it. NOT the drawn `panel-well`:
   that sprite is a dark hole for cream ink (the chat input, the bag's grid
   ground), and this is dark ink on paper. Using it here would have made a
   text list look like an inventory. */
body.skin-majesty .ww-profwell {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 10px 12px 12px;
  background: rgba(255, 246, 225, .42);
  box-shadow: inset 0 0 0 2px rgba(58, 36, 18, .16);
}
body.skin-majesty .ww-profh {
  flex: 0 0 auto;
  font-family: var(--sk-tome);
  font-size: 19px;
  line-height: 1.1;
  color: #3a2412;
  padding-bottom: 6px;
  margin-bottom: 8px;
  border-bottom: 2px solid rgba(58, 36, 18, .22);
}
/* a second heading INSIDE the page (Build) needs air above it and must not
   claim the flex floor the well's own title has */
body.skin-majesty .ww-profbody .ww-profh { margin-top: 14px; }
body.skin-majesty .ww-profbody {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* the gold scrollbar the ladder wears (Wray, 2026-09-09: "I dont like the
   white scroll bar") -- same values, so the two windows scroll alike */
body.skin-majesty .ww-profbody::-webkit-scrollbar { width: 8px; }
body.skin-majesty .ww-profbody::-webkit-scrollbar-track { background: rgba(58, 36, 18, .16); }
body.skin-majesty .ww-profbody::-webkit-scrollbar-thumb {
  background: #8a6d2f;
  border: 1px solid rgba(28, 18, 6, .5);
}
body.skin-majesty .ww-profbody::-webkit-scrollbar-thumb:hover { background: #c49a3e; }
body.skin-majesty .ww-profbody { scrollbar-width: thin; scrollbar-color: #8a6d2f rgba(58, 36, 18, .16); }

/* ── a label/value line, the shape every page here uses ── */
body.skin-majesty .ww-profrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
  font-size: 16px;
  line-height: 1.25;
  color: #3a2a18;
}
body.skin-majesty .ww-profk { color: #5a4328; }
body.skin-majesty .ww-profv {
  color: #2a1c0c;
  font-variant-numeric: tabular-nums;
}
/* Albion groups its rows and separates the groups with air, which is what
   makes a forty-line readout scannable. Same trick, same reason. */
body.skin-majesty .ww-profgap { height: 12px; }

/* ── the knockout and Chronicle lists ──
   Three parts, not two: who, how, and when. A grid rather than
   space-between so the right-hand pair lines up down the column instead of
   floating wherever the name's length leaves them. */
body.skin-majesty .ww-profcast {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: 10px;
  padding: 5px 0;
  font-size: 16px;
  border-bottom: 1px solid rgba(58, 36, 18, .10);
}
/* the killing blow, in the bitmap face so it reads as a keyword rather than
   as part of the sentence. Empty for an unattributed KO, and an empty span
   still holds its column so the times stay aligned. */
body.skin-majesty .ww-profvia {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  color: #5a4328;
  white-space: nowrap;
}

/* ⭐WHAT FEEDS THIS LIST, SAID OUT LOUD. A short kill list means a quiet week,
   a cat who does not fight, or a mode that keeps no score, and only the window
   can tell those apart. Wray, 2026-09-09: "even if the tab is blank we at
   least know it needs wiring and shipping." */
body.skin-majesty .ww-profnote {
  margin: 12px 0 0;
  font-size: 18px;
  line-height: 1.35;
  color: #5a4328;
  border-top: 1px solid rgba(58, 36, 18, .14);
  padding-top: 8px;
}
body.skin-majesty .ww-profcast:last-child { border-bottom: 0; }
body.skin-majesty .ww-profcastn {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #3a2a18;
}
body.skin-majesty .ww-profcastp {
  flex: 0 0 auto;
  color: #6b2226;
  font-variant-numeric: tabular-nums;
}

/* ── the three badges. A locked one is dimmed, never hidden: the point of
   showing it is that it can be got. ── */
body.skin-majesty .ww-profbadge.is-earned .ww-profv { color: #8a6d2f; }
body.skin-majesty .ww-profbadge:not(.is-earned) { opacity: .5; }

/* ── their build, as three chips ──
   The rarity is the RIM, which is where this window's bag grid puts it and
   what Wray picked there ("IF we could have A with a rarity border that might
   look better"). Off the same RARITY map in world-windows.js, so a legendary
   is one colour across the whole UI. */
body.skin-majesty .ww-profbuild {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
body.skin-majesty .ww-profcard {
  flex: 0 1 auto;
  min-width: 0;
  padding: 5px 8px;
  font-size: 14px;
  line-height: 1.2;
  color: #f6ecd6;
  background: #1b2430;
  box-shadow: inset 0 0 0 2px var(--r, transparent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ON A PHONE IT LANDS ON THE LADDER, ribbons in a row along its top -- there
   is no room beside a window that is already the picture less 16px. The
   position comes from `place()` in world-windows.js at the same breakpoint;
   this is the shape. */
@media (max-width: 900px) {
  body.skin-majesty .ww-profile { border-width: 20px; border-image-width: 20px; }
  /* clear the row of ribbons the strip becomes at this width */
  body.skin-majesty .ww-profwell { margin-top: 44px; }
  /* ⛔AND THE ROW COMES OUT FROM UNDER THE CARD. 61-against-62 is the tuck
     that makes a hanging ribbon look like it goes behind the parchment, and
     it is exactly wrong once the strip is a row INSIDE the window: measured at
     390x664, all four ribbons rendered under 28px of paper and the window had
     no visible navigation at all. */
  body.skin-majesty .ws-tabs.ww-proftabs { z-index: 63; }
}

/* ══════════════════════════════════════════════════════════════════════
   NATIVE WINDOW TEXT ON THE SYSTEM SURFACE (#2020, 2026-09-09)

   The dark surface already flips `--sk-ink` and `--sk-ink-dim` ON the window,
   so everything inside INHERITS the right colour -- and that was enough while
   the only centred door was the Store, which frames pages in iframes.

   ⛔THE SPELLBOOK IS THE FIRST CENTRED DOOR THAT DRAWS ITS OWN CONTENT, and
   inheritance does nothing for an element that sets `color` explicitly. Every
   rule below hardcoded a parchment brown, so flipping the door would have
   rendered #6b5233 text on a #2a2118 ground: the geometry perfect, the frame
   perfect, the words invisible. Nothing would have thrown and no test that
   existed would have failed.

   ⭐THE LIST IS SHORT ON PURPOSE, and the reason is worth knowing before
   adding to it: most of this window's content sits on its OWN ground rather
   than the window's. `.ww-cell` paints paper.png behind every card tile, and
   `.ww-slotc` draws the slot sprite, so their dark ink is still correct over
   their own parchment and MUST NOT be flipped. Only text drawn straight onto
   the window needs an answer here. Five rules, not a sweep.

   The next native centred window extends this block. worldSpellbookCentred
   .test.js fails if one of these classes loses its counterpart.
   ══════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════
   HELP IS THE TUTORIAL, WRITTEN OUT (#2769, 2026-09-13)
   ──────────────────────────────────────────────────────────────────────
   A numbered list of the beats Nerm actually says, and one button that
   walks them again. Same row shape as the quest log — a mark, a body, and
   nothing pressable except the one control — because they are the same
   kind of object: a short list of things to do, in order.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-help { padding: 2px 0 8px; }
body.skin-majesty .ww-help-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 0 2px 6px;
  margin: 0 0 8px;
  border-bottom: 2px solid rgba(122, 90, 56, .35);
}
body.skin-majesty .ww-help-head b {
  font-family: var(--sk-head);
  font-size: 9px;
  letter-spacing: 1px;
  color: #8a6420;
}
body.skin-majesty .ww-help-state {
  font-family: var(--sk-body);
  font-size: 17px;
  color: #5a4328;
}
body.skin-majesty .ww-help-sub {
  font-family: var(--sk-body);
  font-size: 17px;
  line-height: 1.2;
  color: #5a4328;
  margin: 0 2px 10px;
}
/* the one control, the plate every other button in the skin wears */
body.skin-majesty .ww-help-go {
  display: block;
  width: 100%;
  min-height: 44px;              /* #2535's tap floor */
  margin: 0 0 12px;
  font-family: var(--sk-head);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--sk-ink);
  cursor: pointer;
  border-style: solid;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 10px;
  border-width: 10px;
  border-image-repeat: repeat;
  background: none;
  image-rendering: pixelated;
}
body.skin-majesty .ww-help-go:hover { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ww-help-go:active { border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a'); }
body.skin-majesty .ww-help-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 4px;
  border-bottom: 1px solid rgba(122, 90, 56, .18);
}
body.skin-majesty .ww-help-row:last-child { border-bottom: 0; }
body.skin-majesty .ww-help-n {
  flex: 0 0 auto;
  width: 20px;
  font-family: var(--sk-head);
  font-size: 9px;
  line-height: 1.5;
  color: #8a6420;
  text-align: center;
}
body.skin-majesty .ww-help-t {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--sk-body);
  font-size: 17px;
  line-height: 1.2;
  color: #2e2113;
}
/* a beat already walked goes quiet, the way a done quest does */
body.skin-majesty .ww-help-row.is-done .ww-help-t,
body.skin-majesty .ww-help-row.is-done .ww-help-n { color: #9d8a6d; }

/* ══════════════════════════════════════════════════════════════════════
   THE HOUSE WINDOW (#2770, 2026-09-13)
   ──────────────────────────────────────────────────────────────────────
   The house you are looking at, big; the nine as a rail you tap through;
   one control. The house's own colour is the only colour in here, set as
   `--hc` on the stage so the sigil's glow, the name and the rail's live
   pip all read as one house rather than three tints that have to be kept
   in step.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-house { padding: 2px 0 8px; }
body.skin-majesty .ww-house-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
  padding: 6px 4px 10px;
}
body.skin-majesty .ww-house-badge {
  font-family: var(--sk-head);
  font-size: 8px;
  letter-spacing: 1px;
  color: var(--hc, #8a6420);
}
/* the sigil, and the only glow in the skin: a house IS its light */
body.skin-majesty .ww-house-orb {
  width: 118px;
  height: 118px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--hc, #8a6420);
  background: radial-gradient(circle at 50% 45%,
    color-mix(in srgb, var(--hc, #8a6420) 22%, transparent), transparent 70%);
  image-rendering: pixelated;
}
body.skin-majesty .ww-house-orb img { width: 86px; height: 86px; object-fit: contain; }
body.skin-majesty .ww-house-name {
  font-family: var(--sk-tome);
  font-size: 26px;
  line-height: 1;
  color: var(--hc, #2e2113);
}
body.skin-majesty .ww-house-role {
  font-family: var(--sk-head);
  font-size: 8px;
  letter-spacing: 1px;
  color: var(--hc, #8a6420);
}
body.skin-majesty .ww-house-flav {
  font-family: var(--sk-body);
  font-size: 17px;
  line-height: 1.2;
  font-style: italic;
  color: #5a4328;
  margin: 2px 0;
}
body.skin-majesty .ww-house-sworn {
  font-family: var(--sk-body);
  font-size: 16px;
  color: #8a7355;
}
/* the price of the button above it (#3026) */
body.skin-majesty .ww-house-price {
  font-family: var(--sk-body);
  font-size: 16px;
  color: #8a6420;
  margin-top: 2px;
}
body.skin-majesty .ww-house-cta {
  width: 100%;
  min-height: 44px;              /* #2535's tap floor */
  margin-top: 6px;
  font-family: var(--sk-head);
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--sk-ink);
  cursor: pointer;
  border-style: solid;
  border-image-source: url('/assets/ui/btn-idle.png?v=2026-08-29a');
  border-image-slice: 5 fill;
  border-image-width: 10px;
  border-width: 10px;
  border-image-repeat: repeat;
  background: none;
  image-rendering: pixelated;
}
body.skin-majesty .ww-house-cta:hover:not(:disabled) { border-image-source: url('/assets/ui/btn-on.png?v=2026-08-29a'); }
body.skin-majesty .ww-house-cta:active:not(:disabled) { border-image-source: url('/assets/ui/btn-press.png?v=2026-08-29a'); }
body.skin-majesty .ww-house-cta:disabled { opacity: .55; cursor: default; }
/* the nine, three across: at the dock's width a single row of nine would be
   28px pips, under the tap floor and too small to read a sigil in */
body.skin-majesty .ww-house-rail {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  padding: 8px 4px 4px;
  border-top: 2px solid rgba(122, 90, 56, .35);
}
body.skin-majesty .ww-house-pip {
  min-height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 2px solid rgba(122, 90, 56, .35);
  background: rgba(0, 0, 0, .06);
  image-rendering: pixelated;
}
body.skin-majesty .ww-house-pip img { width: 34px; height: 34px; object-fit: contain; }
body.skin-majesty .ww-house-pip.is-on { border-color: var(--hc, #8a6420); }
body.skin-majesty .ww-house-pip.is-mine { background: rgba(202, 169, 130, .18); }
body.skin-majesty .ww-house-msg {
  font-family: var(--sk-body);
  font-size: 17px;
  color: #8a6420;
  text-align: center;
  min-height: 20px;
  padding: 4px 2px 0;
}
body.skin-majesty .ww-house-note {
  font-family: var(--sk-body);
  font-size: 15px;
  line-height: 1.2;
  color: #8a7355;
  margin: 6px 2px 0;
  text-align: center;
}

body.skin-majesty .ws-sheet[data-surface='system'] .ww-none,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-state,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-spellsnote p,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-sub,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-state,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-house-sworn,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-house-note {
  color: var(--sk-ink-dim);
}
/* House always opens on the system surface: its flavour line and its rail
   need the dark answer, and the rail's rule is the same hairline the help
   rows take. */
body.skin-majesty .ws-sheet[data-surface='system'] .ww-house-flav,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-house-price {
  color: var(--sk-ink);
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-house-rail {
  border-top-color: rgba(232, 213, 169, .22);
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-house-pip {
  border-color: rgba(232, 213, 169, .22);
  background: rgba(0, 0, 0, .25);
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-t {
  color: var(--sk-ink);
}
/* Help always opens on the system surface, so its rows need the dark
   answer: the hairline that reads on parchment disappears on navy, and a
   walked beat has to stay legible rather than sink into the ground. */
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-row {
  border-bottom-color: rgba(232, 213, 169, .16);
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-row.is-done .ww-help-t,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-row.is-done .ww-help-n {
  color: #8a7c63;
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-help-head {
  border-bottom-color: rgba(232, 213, 169, .22);
}

/* the grid's scrollbar was a warm brown for the parchment; on the dark ground
   it is the same gold the ladder and the profile use */
body.skin-majesty .ws-sheet[data-surface='system'] .ww-grid {
  scrollbar-color: #8a6d2f rgba(0, 0, 0, .28);
}

/* ── THE SPELLBOOK'S GRID, NOW THAT IT HAS ROOM (#2020, 2026-09-09) ──
   `.ww-grid`'s `minmax(38px, 1fr)` was sized for a ~300px docked column and
   packed ten cards a row into a 444px one -- 41px tiles carrying two letters
   each. The window is a centred browsing window now, so the cells become
   CARD-SHAPED and big enough to read: six across, the width Wray picked for
   the bag ("I think 6 items wide like albion is better for the bag").
   Scoped to `.ww-spells` so the bag's own grid, which he tuned separately, is
   untouched. */
body.skin-majesty .ww-spells .ww-grid {
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 6px;
}
body.skin-majesty .ww-spells .ww-cell {
  /* portrait, because a card is. 0.72 is 1/1.4, the proportion the card art
     is drawn at; `aspect-ratio` is width/height so the reciprocal is the one
     that goes here -- writing 1.4 gives a LANDSCAPE cell, which is the easy
     mistake and looks deliberate. */
  aspect-ratio: .72;
  position: relative;
  /* ⛔NOT `hidden` (#3031 D1). The grid's height is fixed by the window, and
     an item that clips has no automatic minimum size, so the rows shrank
     under the cells: 91px cards on a 45px pitch at 1920, and 8px strips on a
     phone. The art sits inside an inset, so nothing here needs the clip. */
  overflow: visible;
}
/* the picture covers the tile and the rarity rim stays on top of it, which is
   what makes a shelf of cards scannable by colour before you read a name */
body.skin-majesty .ww-spells .ww-cellart {
  position: absolute;
  inset: 3px;
  width: calc(100% - 6px);
  height: calc(100% - 6px);
  object-fit: cover;
  image-rendering: pixelated;
  pointer-events: none;
}
/* the initials sit UNDER the art and show through only when it fails: they
   are the fallback for the 9 spells of 93 that have no picture at all */
body.skin-majesty .ww-spells .ww-abbr {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ── WHAT A WEEKLY TIER PAYS (#2020, 2026-09-09) ──
   The daily rows have carried `+25` since they were built; the weekly ones
   showed nothing, so four rows read "3 quests / Keep going." with no reason
   to care about any of them. Both numbers were in the payload already.
   A column rather than a row: the pack is a second, different reward, and
   putting it beside the GP on one line reads as one figure. */
body.skin-majesty .ww-q-rew {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  align-self: center;
}
/* the pack is the rarer half and says so: it is the only source of new cards
   in the game, so it wears the gold the claim button does rather than the
   quieter ink the GP figure uses */
body.skin-majesty .ww-q-pack {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 8px;
  letter-spacing: .04em;
  color: #8a6d2f;
  white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════════════
   THE LADDER'S CREST COLUMN (#2020, Wray 2026-09-09)

   "the score doesnt render at the end, it squashes the house name and player
   name. replace house name with a house logo and guild logo there."

   ⛔THE SQUASH WAS A MIS-TRACKED GRID, shipped the day before. The rule above
   reserves FIVE tracks so the movement arrow keeps the score aligned down the
   ladder -- but the arrow was only appended on rows that MOVED, so every other
   row had four children, the score landed in the arrow's `auto` track and the
   72px score track sat empty at the end. Measured at 1920: the score ended at
   x331 in a row ending at x415, squeezed to 34px, with 84px of nothing after
   it. world-windows.js now always inserts the span, empty when it did not
   move, which holds the column without drawing anything.

   ⭐AND THE CREST IS THE OTHER HALF. "Manastorm" is 47px of text in a 357px
   row; the sigil says the same in 18px and is faster to read down a column of
   twenty-five. Fixed width, so nothing jumps between rows.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-crest {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  /* room for two marks; the guild half is usually absent and costs nothing */
  min-width: 22px;
}
body.skin-majesty .ww-crest-h,
body.skin-majesty .ww-crest-g {
  width: 18px;
  height: 18px;
  object-fit: contain;
  image-rendering: pixelated;
  flex: 0 0 auto;
  display: block;
}
/* the guild mark reads as the SECOND thing: a house is who you are, a guild is
   who you run with, and the ladder is sorted by neither */
body.skin-majesty .ww-crest-g { opacity: .9; }
/* a guild with a tag but no art yet: three characters, not a hole */
body.skin-majesty .ww-crest-t {
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 7px;
  letter-spacing: .04em;
  color: #5a4328;
  white-space: nowrap;
}
/* the top three sit on a plate; a crest on it needs to stay legible */
body.skin-majesty .ww-rank.is-top .ww-crest-t { color: #4a3312; }

/* ⛔THE SCORE CLEARS THE PLATE'S RIVET (#2020, 2026-09-09).
   `.ww-rank.is-top::after` is a 5px stud at `right: 4px` with `z-index: 1`,
   decoration on the gold/silver/bronze plate. It has been there since the
   podium was built and never touched anything, because the score used to stop
   84px short of the row's end -- the mis-tracked grid fixed above. Giving the
   score its real 72px column ran the number straight under the stud, so the
   top three read "31726" and "19658" with the last digit swallowed while
   every row below them was fine.

   The padding goes on EVERY row, not just the plated ones: it keeps the
   column aligned down the whole ladder, which was the point of fixing the
   tracks in the first place. 12px clears the stud's 4-9px with room. */
body.skin-majesty .ww-rank .ww-score { padding-right: 12px; }

/* ── THE TRADES, ON PARCHMENT (#2681, 2026-09-09) ──
   The track rows were written for the Character window, which is dark, so
   their ink comes from `var(--sk-ink)` -- cream. Rendered in the bag's
   companion panel, which is parchment, that is cream on cream: the labels went
   pale and "0 / 25" nearly vanished. It is the same fault the Spellbook hit
   this morning, arriving from the opposite direction, and it has the same
   shape of answer -- the component does not choose its ground, so the ground
   states its ink.

   `.wc-window` already carried exactly these overrides for the dark window.
   Rather than a third copy, the panel joins that selector list.

   AND THE WIDTH: `.ww-asidebody` is `display: flex` (a row), so `.wc-skills`
   sized to its content and three full-width bars rendered as three stubs in a
   520px panel. One `flex` declaration, not a width. */
body.skin-majesty .ww-aside .wc-track-head b,
body.skin-majesty .ww-aside .wc-track-lv { color: #2e2113; }
body.skin-majesty .ww-aside .wc-track-note { color: #5a4328; }
body.skin-majesty .ww-aside .wc-track-bar {
  background: rgba(46, 33, 19, .16);
  box-shadow: inset 0 0 0 1px rgba(46, 33, 19, .28);
}
body.skin-majesty .ww-aside .wc-skills {
  color: #5a4328;
  flex: 1 1 auto;
  min-width: 0;
  align-content: flex-start;
}
/* a trade you have not started reads as a trade, not as a broken bar */
body.skin-majesty .ww-aside .wc-track { padding-bottom: 4px; }
/* the weapon lines under the trades (#3585): one small heading, the same rows */
body.skin-majesty .wc-skills-group {
  display: block;
  margin: 14px 0 0;
  font-family: var(--sk-body, 'VT323', monospace);
  font-size: 15px;
  letter-spacing: .06em;
  text-transform: uppercase;
}
body.skin-majesty .ww-aside .wc-skills-group { color: #2e2113; }
/* ⛔ the aside's body is a ROW (.ww-asidebody above), so a second block would
   sit beside the trades, not under them (the review on #3597). The Skills
   panel stacks: the trades, then the weapon lines, scrolling when they do
   not fit, each list its own height rather than a column-filling flex */
body.skin-majesty .ww-aside[data-aside='skills'] .ww-asidebody {
  flex-direction: column;
  overflow-y: auto;
}
body.skin-majesty .ww-aside[data-aside='skills'] .wc-skills { flex: 0 0 auto; }

/* ══════════════════════════════════════════════════════════════════════
   THE $9LV PANEL, LEGIBLE ON THE DARK SURFACE (#2020, Wray 2026-09-09)

   "the 9LV page is looking a bit hard to read text probably needs bumpin up
   at least on the smaller"

   ⛔IT IS THE INK, AND THE SIZE IS THE SYMPTOM HE NAMED. Measured in the
   Store frame: "no wallet connected" rendered #2e2113 and "1000M $9LV"
   rendered #3a2a18 -- parchment browns on a #2a2118 ground. The panel is
   native content built for parchment and the Store is centred, so it is dark.

   ⭐THIS IS THE THIRD TIME TODAY, and the third file. The Spellbook hit it
   going one way (dark ink on its new dark ground) and the professions panel
   hit it going the other (cream ink on the bag's parchment). The pattern is
   worth naming: a component does not choose its ground, so the ground has to
   state its ink -- and any `.ww-*` block that hardcodes a colour will be
   wrong on one of the two surfaces. Sized text is the same story: 7px was
   chosen against parchment's contrast.

   ⛔`--sk-ink` IS NOT ENOUGH ON ITS OWN. The system window declares it and
   everything INHERITS it, which does nothing for a rule that sets `color`
   itself. Every line below is one of those.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ws-sheet[data-surface='system'] .ww-token,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-ca-addr,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-lock,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-bal-v {
  color: var(--sk-ink);
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-token .ww-h,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-h {
  color: #e8d5a9;
}
body.skin-majesty .ws-sheet[data-surface='system'] .ww-ca-tag,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-check .ww-k,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-supply .ww-k,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-keyi i,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-lock i,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-k {
  color: var(--sk-ink-dim);
}
/* the accents keep their MEANING and gain the contrast the dark ground needs:
   red still means look here, green still means settled */
body.skin-majesty .ws-sheet[data-surface='system'] .ww-ca-copy,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-more,
body.skin-majesty .ws-sheet[data-surface='system'] .ww-lock-amt { color: #e08a86; }
body.skin-majesty .ws-sheet[data-surface='system'] .ww-check.is-ok .ww-v { color: #7fc98a; }
body.skin-majesty .ws-sheet[data-surface='system'] .ww-check.is-warn .ww-v { color: #e08a86; }
body.skin-majesty .ws-sheet[data-surface='system'] .ww-bal b { color: #d8b45e; }

/* ⭐AND THE SMALLEST TEXT COMES UP. Press Start 2P is a chunky face and it
   was set at 7px; VT323 is a narrow one and it was set at 12. Both were
   picked against parchment, where dark-on-light carries a size this ground
   cannot. These four rules are NOT surface-scoped: the token panel only ever
   opens in the Store, and 7px was too small on parchment too. */
body.skin-majesty .ww-token .ww-ca-tag { font-size: 9px; }
body.skin-majesty .ww-token .ww-k { font-size: 9px; }
body.skin-majesty .ww-token .ww-bal b { font-size: 10px; }
body.skin-majesty .ww-token .ww-lock i,
body.skin-majesty .ww-token .ww-keyi i { font-size: 15px; line-height: 1.35; }

/* ══════════════════════════════════════════════════════════════════════
   THE SPELLBOOK'S CARDS TAB, CENTRED, AND ITS EMPTY STATE DRAWN AS SLOTS
   (#2557, 2026-09-22). The build row sat in the top-left corner of a
   centred .43 window with one line of text in the middle. Now the head,
   the three slots and the hint centre on the window's axis, and an empty
   collection shows eight empty cells and a plank that opens the Store,
   which is Albion's pattern: the panel keeps its shape when it has
   nothing in it.
   ══════════════════════════════════════════════════════════════════════ */
body.skin-majesty .ww-spells { align-items: center; }
body.skin-majesty .ww-spells .ww-grid { width: 100%; }
body.skin-majesty .ww-emptyslots {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 6px;
  max-width: 520px;
  margin: 8px auto 0;
  width: 100%;
}
body.skin-majesty .ww-cell.is-empty {
  aspect-ratio: 3 / 4;
  background: rgba(0, 0, 0, .06);
  box-shadow: none;
  border: 2px dashed rgba(90, 67, 40, .45);
  box-sizing: border-box;
  cursor: default;
  pointer-events: none;
}
body.skin-majesty .ww-spells .ww-none { text-align: center; }
body.skin-majesty .ww-emptycta {
  grid-column: 1 / -1;
  justify-self: center;
  margin-top: 4px;
  min-width: 160px;
  min-height: 44px;
  font-family: var(--sk-head, 'Press Start 2P', monospace);
  font-size: 9px;
  color: var(--sk-ink, #e6c88a);
  text-shadow: 0 2px 0 #000;
  cursor: pointer;
}
}
/* end of @layer skin */
