/* atrament.us: Press Start 2P is self-hosted (OFL, fonts/PressStart2P-OFL.txt) instead of the Google
   Fonts import. Bungee, Kalam and Comic Neue were fallbacks only and are no longer loaded. */
@font-face {
  font-family: 'Press Start 2P';
  src: url('../fonts/PressStart2P-Regular.ttf') format('truetype');
  font-display: swap;
}

/* Self-hosted header/body fonts, copied over from a Natural Grid neocities
   theme (by 5493 Arts) at the user's request: Origami Mommy for the display
   font, Pixel Square for the body font. Both are free-for-personal-use fonts
   from dafont.com - keep the credit if this ever goes public.
   Self-hosting (rather than a Google Fonts @import) also means these two
   load offline once cached, instead of depending on a network fetch. */
@font-face {
  font-family: 'Origami Mommy';
  src: url('../fonts/origami-mommy.ttf') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Square';
  src: url('../fonts/pixel-square.ttf') format('truetype');
  font-display: swap;
}
/* Same round, added when the user asked to also re-font the profile-meta
   line, tagline, lookbook status, and every slot/clothing name. Picked from
   the rest of the dropped font folder by rendering comparison sheets at the
   actual sizes each role uses, rather than guessing - Pixel Operator reads
   cleanly even at ~11px, unlike most of the other candidates in the folder. */
@font-face {
  font-family: 'Pixel Operator';
  src: url('../fonts/PixelOperator.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Operator';
  src: url('../fonts/PixelOperator-Bold.ttf') format('truetype');
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Operator Mono';
  src: url('../fonts/PixelOperatorMono.ttf') format('truetype');
  font-display: swap;
}

:root {
  /* Third palette pass, replacing the hot-magenta/cyan/yellow "bling" set
     with the user's own picked pastels. Named tokens below are the five
     colors as given; everything else (--ink/--text/--danger/--accent-3/
     --accent-4) is a derived shade built to stay in the same warm, dusty
     family while still clearing 4.5:1 text contrast where it's used as a
     foreground color - checked with the actual WCAG formula, not eyeballed. */
  --alabaster-grey: #d8e2dc;
  --powder-petal: #ffe5d9;
  --pastel-pink: #ffcad4;
  --cherry-blossom: #f4acb7;
  --dusty-mauve: #9d8189;
  /* Warm blush-grey, blended from alabaster-grey + pastel-pink - used for the
     background checkerboard instead of alabaster-grey on its own, which
     read as a cool grey outlier against the rest of the warm pink family. */
  --checker-tint: #efd7da;

  --bg: var(--powder-petal);
  --panel: #fff6f0;   /* a warm near-white, not stark white - the flat white masthead was one of the specific complaints */
  --panel-2: var(--pastel-pink);
  --accent: var(--cherry-blossom);
  --accent-2: var(--dusty-mauve); /* border/shadow accent only - a bit too mid-tone for solid text backgrounds, see usage notes below */
  --accent-3: #f0b884; /* derived warm apricot-gold, for sparkle/highlight/hazard-stripe roles the pale given colors were too low-contrast for */
  --accent-4: #8fae9c; /* derived sage-green from the alabaster-grey family, for "positive/online" status */
  --text: #4a3639;
  --text-dim: #6e575b;
  --danger: #9c3a47;
  --ink: #2e2124;      /* deep warm brown-black outline color, replacing the old cool purple-black */
  /* Dark UI chrome (status bar, bottom nav, toast, the dark rail widget) used
     to mix --ink with a couple of one-off browns (#3a2a2e/#554044) that read
     as muddy/purple rather than clearly part of the pink family - flagged as
     "ugly brown/purple boxes". These two are deliberately on the same hue as
     cherry-blossom (~351°) just pushed dark and saturated, so every dark
     surface on the page now comes from one consistent deep-rose family
     instead of an unrelated near-black brown. --ink itself is left alone
     since it's doing a different job (thin borders/outlines/text), not
     large fill areas. */
  --surface-dark: #3f1219;
  --surface-dark-2: #5c1f28;
  --radius: 16px;
  --tap: 50px;

  /* Fourth pass: header and body fonts copied from a reference theme's own
     page source at the user's request. Origami Mommy is a bold rounded
     pixel-block display face - it actually reinforces the "pixel" branding
     better than any of the three earlier guesses did. Pixel Square tested
     as legible even at 12px with full lowercase support (checked by
     rendering both, not guessing) unlike its neighboring candidates, so it
     took over as the sitewide body font. Bungee/Comic Neue stay in the
     fallback chain as a safety net if the local files fail to load. */
  --font-display: 'Origami Mommy', 'Bungee', 'Comic Sans MS', system-ui, sans-serif;
  /* Tagline + lookbook-status: was the handwritten Kalam script, now Pixel
     Operator so the page's few remaining non-pixel text roles fall in line
     with the rest of the branding. Kalam stays as a fallback. */
  --font-flourish: 'Pixel Operator', 'Kalam', 'Comic Sans MS', cursive;
  --font-body: 'Pixel Square', 'Comic Neue', 'Comic Sans MS', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-pixel: 'Press Start 2P', ui-monospace, monospace; /* genuine bitmap font - used for the sparkle stars so "pixelated" is literal, not faked with a shape */
  /* Slot labels + every clothing item name, wherever it appears (current
     item, library/favorites cards, the picker sheet, the lock list) - bold
     weight so these functional identifiers stay a notch more prominent than
     plain Pixel Square body paragraphs. */
  --font-label: 'Pixel Operator', 'Comic Neue', sans-serif;
  /* The small fake "est. 2026 · online" metadata line only - a quieter
     monospace/terminal cut instead of the bold UI label font. */
  --font-meta: 'Pixel Operator Mono', Georgia, 'Times New Roman', serif;
}

* { box-sizing: border-box; }

html {
  /* Global size bump after feedback that text was near-unreadable at 100%
     desktop zoom - this scales every rem-based font-size sitewide by ~19%
     in one place. The very smallest labels (nav text, rail-widget text,
     slot labels) get an extra bump of their own further down, since 19%
     of a tiny number is still a tiny number. */
  font-size: 19px;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  /* Pixel Square is a bitmap-style font - a bit more line-height than a
     humanist sans needs keeps multi-line hints/paragraphs from feeling
     cramped, since its glyphs read denser than Comic Neue's did. */
  line-height: 1.5;
  height: 100%;
  overscroll-behavior-y: contain;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding-bottom: env(safe-area-inset-bottom);
  /* very faint checkerboard wash behind everything - barely-there, so it
     never competes with the character preview */
  background-image:
    linear-gradient(45deg, var(--checker-tint) 25%, transparent 25%),
    linear-gradient(-45deg, var(--checker-tint) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--checker-tint) 75%),
    linear-gradient(-45deg, transparent 75%, var(--checker-tint) 75%);
  background-size: 28px 28px;
  background-position: 0 0, 0 14px, 14px -14px, -14px 0;
}

/* Reusable chunky checkerboard ribbon - the loud version, for dividers */
.checker-strip {
  height: 11px;
  width: 100%;
  flex-shrink: 0;
  background-image:
    linear-gradient(45deg, var(--ink) 25%, transparent 25%),
    linear-gradient(-45deg, var(--ink) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--ink) 75%),
    linear-gradient(-45deg, transparent 75%, var(--ink) 75%);
  background-color: var(--accent-3);
  background-size: 11px 11px;
  background-position: 0 0, 0 5.5px, 5.5px -5.5px, -5.5px 0;
}

/* Pixelated sparkle stars - a real bitmap font glyph, so the "pixelated"
   look is literal instead of an approximated shape. */
.pixel-star {
  display: inline-block;
  font-family: var(--font-pixel);
  font-size: 0.7em;
  line-height: 1;
  color: var(--accent-3);
  animation: twinkle 1.6s ease-in-out infinite;
}
.pixel-star::before { content: "\2605"; } /* BLACK STAR - Press Start 2P renders it as a chunky pixel glyph */
.pixel-star.pink { color: var(--accent); animation-delay: .3s; }
.pixel-star.cyan { color: var(--accent-2); animation-delay: .6s; }
.pixel-star.yellow { color: var(--accent-3); animation-delay: .45s; }
.pixel-star.corner {
  position: absolute;
  font-size: 0.85rem;
}
@keyframes twinkle {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(0.7); opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  .pixel-star { animation: none; }
}

/* The header used to be a boxed panel (background + border-bottom) wrapping
   the title - which, once the title became a single image that already has
   its own rounded-corner border baked into the art, meant a box sitting
   inside another box. Round 14 dropped the panel background/border, but
   left the banner as a narrower, centered, padded image - which still read
   as a smaller rounded box floating inside the outer card's own rounded
   box, just with both boxes visible now instead of one being invisible
   white. This round: no side/top padding at all, and no max-width cap, so
   the banner is a true edge-to-edge strip that IS the top of the card
   (or the top of the screen on mobile) rather than a graphic sitting inside
   it with a gap all around. The corner expand-preview button that used to
   live here moved onto the lookbook module's own window-controls (it
   expands the character art, so it belongs on the character art's window,
   not the site title). */
.app-header {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  padding-top: env(safe-area-inset-top);
  position: relative;
}
.header-titles { text-align: center; width: 100%; }
/* The title/tagline text was replaced with a single hand-drawn pixel-art
   banner image the user supplied (title lettering, tagline, and a whole
   scene of decorative flourishes - clothes rack, mirror, bows, sparkles -
   already baked into the art itself), after two rounds of CSS-built
   text+decoration never quite landing. It arrived as a flat PNG with a
   black fill outside its rounded corners; chroma-keyed that background out
   in Python before saving it here, so it sits cleanly on any backdrop. */
.masthead-banner {
  margin: 0;
  line-height: 0;
}
.masthead-banner img {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

.header-titles { min-width: 0; }
.online-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-4);
  box-shadow: 0 0 0 2px #e4ede8, 0 0 4px var(--accent-4);
  flex-shrink: 0;
}
/* "est. 2026 - mood: ..." used to live directly under the header title, but
   once the banner went edge-to-edge (with no bottom padding left in the
   header for it to sit in) it ended up half-covered by the checker-strip
   divider. Small print like this belongs at the bottom of the page anyway -
   moved it to a real footer, the same spot copyright text would go on an
   old personal site. */
.site-footer {
  text-align: center;
  font-family: var(--font-meta);
  font-size: 0.72rem;
  color: var(--text-dim);
  padding: 8px 16px;
  flex-shrink: 0;
}

/* ---------- Site shell: outer frame + masthead decor + status bar ---------- */

.site-shell {
  position: relative;
}

.masthead-checker {
  /* .app-header is a flex ROW (for centering the title against the corner
     button), so this was originally an in-flow flex child too - which put
     it beside header-titles instead of below it, squeezed the title's
     available width, and wrapped it. Taking it out of flow with absolute
     positioning fixes that at every screen size and drops the old
     negative-margin bleed hack entirely, since full-bleed is just 0/0 now. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  width: auto;
  border-top: 2px solid var(--ink);
}

/* A small dark accent bar under the tagline, mixing real counts (favorited,
   saved outfits, closet size) with purely decorative flavor text - the
   "aesthetically unnecessary" bits old fan/community pages always had. */
.status-bar {
  display: flex;
  /* Wrap the pills onto a second row on narrow screens instead of the old
     nowrap horizontal-scroll strip: the scrollbar was hidden, so the last
     pill ("online") just read as clipped/truncated with no cue that it
     scrolled. Wrapping keeps every pill visible at any count/width. Desktop
     has room for all four on one line, so this has no visible effect there. */
  flex-wrap: wrap;
  gap: 8px;
  padding: 7px 14px;
  background: var(--surface-dark);
  border-top: 2px solid var(--ink);
  border-bottom: 3px solid var(--ink);
}
.stat-chip {
  flex-shrink: 0;
  font-family: var(--font-meta);
  font-size: 0.78rem;
  color: #f3e6de;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.3);
  border-radius: 999px;
  padding: 3px 10px;
  display: flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.stat-chip b { color: var(--accent-3); font-weight: 700; }
.stat-chip .online-dot { margin-left: 1px; }
.stat-flavor { color: #d9c3bc; }

/* Side rails are desktop-only bonus real estate - fully removed from the
   layout on mobile rather than just hidden, so they cost nothing there. */
.site-body { display: block; }
.side-rail { display: none; }

.rail-widget {
  border: 2px solid var(--ink);
  border-radius: 12px;
  overflow: hidden;
  font-size: 0.82rem;
}
.rail-widget-title {
  font-family: var(--font-pixel);
  font-size: 0.62rem;
  letter-spacing: 0.02em;
  padding: 8px 10px;
  text-align: center;
}
.rail-widget-list {
  list-style: none;
  margin: 0;
  padding: 8px 10px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-family: var(--font-meta);
}
.rail-widget-list li {
  padding-left: 16px;
  position: relative;
}
/* A tiny Flair heart image instead of a plain bullet character - one of
   the small "aesthetically unnecessary" touches used liberally per the
   user's Flair folder request. */
.rail-widget-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.15em;
  width: 11px;
  height: 11px;
  background: url('../assets/flair/bullet-heart.gif') center / contain no-repeat;
}
/* Two different panel personalities for two different widgets - pale
   title-strip vs. dark box, matching how Goatlings mixes lavender buttons,
   pale panels, and black boxes rather than one uniform component. */
.rail-widget-light {
  background: var(--panel);
}
.rail-widget-light .rail-widget-title {
  background: linear-gradient(180deg, #fff0f3, var(--pastel-pink));
  color: var(--ink);
  border-bottom: 2px solid var(--ink);
}
.rail-widget-light .rail-widget-list li { color: var(--text); }

.rail-widget-dark {
  background: var(--surface-dark);
}
.rail-widget-dark .rail-widget-title {
  background: linear-gradient(180deg, var(--surface-dark-2), var(--surface-dark));
  color: var(--accent-3);
  border-bottom: 2px solid var(--ink);
}
.rail-widget-dark .rail-widget-list li { color: #f1e2dc; }
/* One throwaway joke line with zero function, just charm - the kind of
   thing eggramen's footer does ("the demon is a lie"). Slightly dimmer and
   italic so it reads as an aside rather than another real status line. */
.rail-widget-joke {
  font-style: italic;
  opacity: 0.7;
}

/* A small "back to main site" link, styled after the glowing/letter-spaced
   link hover treatment from the reference Y2K layout - doesn't have to go
   anywhere real yet, just establishing the convention that this closet
   lives on a bigger personal site. */
.rail-home-link {
  display: block;
  text-align: center;
  padding: 7px 10px 9px;
  font-family: var(--font-meta);
  font-size: 0.75rem;
  color: var(--accent-3);
  text-decoration: none;
  border-top: 1px dotted rgba(255,255,255,.25);
  transition: letter-spacing .15s, text-shadow .15s;
}
.rail-home-link:hover, .rail-home-link:focus-visible {
  letter-spacing: 0.06em;
  text-shadow: 0 0 6px var(--accent-3);
}

.rail-widget-mascot {
  display: block;
  width: 100%;
  height: auto;
  border-bottom: 2px solid var(--ink);
}

.icon-btn {
  background: var(--panel-2);
  border: 2px solid var(--ink);
  color: var(--text);
  width: var(--tap);
  height: var(--tap);
  border-radius: 50%;
  font-size: 1.2rem;
  cursor: pointer;
  box-shadow: 2px 2px 0 var(--ink);
}
.icon-btn:active { box-shadow: none; transform: translate(2px, 2px); }

main#screens {
  flex: 1;
  /* Flex items default to min-height:auto, which means they refuse to
     shrink below their own content's height - so overflow-y:auto below
     never actually engaged; the whole page/body grew instead and the
     browser window scrolled, rather than just this panel. min-height:0
     overrides that default so main can be genuinely capped by its flex
     parent's height and scroll internally like it was meant to. */
  min-height: 0;
  overflow-y: auto;
  padding: 0 16px 96px 16px;
  /* An intentional scrollbar reads as decoration instead of an overflow
     accident: pink checkerboard-ish track, dark-outlined star-yellow thumb.
     Firefox gets the closest two-color equivalent it supports. */
  scrollbar-width: thin;
  scrollbar-color: var(--accent-3) var(--panel-2);
}
main#screens::-webkit-scrollbar { width: 10px; }
main#screens::-webkit-scrollbar-track {
  background: var(--panel-2);
  border-left: 2px solid var(--ink);
}
main#screens::-webkit-scrollbar-thumb {
  background: var(--accent-3);
  border: 2px solid var(--ink);
  border-radius: 6px;
  background-image: radial-gradient(circle at 50% 30%, rgba(255,255,255,.8) 1px, transparent 1.5px);
}
main#screens::-webkit-scrollbar-thumb:hover { background: var(--accent); }

.screen { display: none; }
.screen.active { display: block; }

/* ---------- Creator screen ---------- */

.preview-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 8px auto 12px auto;
}

.canvas-frame {
  /* The sprite itself still sits on a neutral backing (set directly on the
     canvas element below) so its colors read accurately - transparent PNG
     pixels are opaque where the character is drawn, so a livelier frame
     BEHIND it only shows through the empty space around the character,
     never over it. That's what makes it safe to dress this up. */
  background: #e6e6e6;
  border: 3px solid var(--ink);
  border-radius: var(--radius);
  padding: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 4px 4px 0 var(--accent), 8px 8px 0 var(--accent-2);
}

#preview-canvas {
  background: #e6e6e6;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  image-rendering: crisp-edges;
  width: 208px;
  height: 208px;
  touch-action: pan-y;
}

/* The main "lookbook" frame and its fullscreen twin get a personal,
   glittery backdrop instead of flat grey - a soft gradient plus a
   scattering of tiny sparkle dots, like a dressing-room mirror. */
.character-column .canvas-frame,
.fullscreen-overlay {
  background-image:
    radial-gradient(circle at 22% 26%, rgba(255,255,255,.95) 1.5px, transparent 2px),
    radial-gradient(circle at 78% 62%, rgba(255,255,255,.95) 1.5px, transparent 2px),
    radial-gradient(circle at 48% 84%, rgba(255,255,255,.8) 1.5px, transparent 2px),
    radial-gradient(circle at 85% 18%, rgba(255,255,255,.8) 1.5px, transparent 2px),
    radial-gradient(circle at 8% 70%, rgba(255,255,255,.7) 1.5px, transparent 2px),
    radial-gradient(circle at 50% 42%, #fff3ec 0%, #ffe1d3 55%, #f6c9c2 100%);
  background-size: 54px 54px, 62px 62px, 46px 46px, 58px 58px, 66px 66px, 100% 100%;
}
.character-column .canvas-frame { padding: 6px; height: 100%; overflow: hidden; }

/* The whole editor lives inside one "module" - a website-widget treatment
   (glossy titlebar + beveled body) that echoes the header's checker/star
   language, so header and editor read as one page instead of a plain app
   screen bolted under a themed banner. It used to also have four small
   semi-transparent checkerboard corner brackets - dropped entirely, since
   they read as confusing rather than decorative, especially sitting this
   close to the *real* transparency checkerboard used elsewhere on item
   thumbnails to mean "this PNG has see-through pixels" - two different
   things trying to use the same visual language was the actual problem. */
.lookbook-module {
  background: var(--panel);
  border: 3px solid var(--ink);
  border-radius: var(--radius);
  padding: 10px 10px 14px;
  margin: 8px 0 10px;
  position: relative;
  /* offset drop shadows for the sticker look, plus a thin inset highlight
     ring just inside the border - one more "nested frame" layer. */
  box-shadow: 5px 5px 0 var(--accent-3), -3px 3px 0 var(--accent-2), inset 0 0 0 2px rgba(255,255,255,.6);
  overflow: hidden;
}

.lookbook-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: -10px -10px 10px;
  padding: 7px 12px;
  background: linear-gradient(180deg, #fff0f3 0%, var(--pastel-pink) 45%, var(--cherry-blossom) 100%);
  border-bottom: 3px solid var(--ink);
  border-radius: calc(var(--radius) - 3px) calc(var(--radius) - 3px) 0 0;
  position: relative;
}
.lookbook-title-group {
  display: flex;
  /* Stacked on mobile: side-by-side, the title ("★ JAMES' LOOKBOOK ★") and the
     status ("wearing N pieces today") each needed more room than half the
     ~300px titlebar could give, so both were ellipsizing. Stacked, each gets
     the full width and fits. Restored to an inline baseline row on desktop
     (see the min-width:800 block), where there's plenty of room. */
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
  overflow: hidden;
}
.lookbook-title {
  font-family: var(--font-pixel);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lookbook-status {
  font-family: var(--font-flourish);
  font-size: 0.95rem;
  color: #7a3b45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A little desktop-window chrome in the corner of the titlebar, borrowed
   from old fake-OS-window personal page layouts. wc-min/wc-close are still
   purely decorative (aria-hidden individually, dropped on narrow phones to
   avoid crowding the title) - but wc-max is the real "expand preview"
   control now: it used to sit as its own button up in the site header, far
   from the character art it actually expands, which didn't make much
   sense. It lives on the character art's own window titlebar now, so it
   stays visible and tappable at every width, unlike its decorative
   siblings, and gets a bit more room to make it a reasonable tap target. */
.window-controls {
  display: flex;
  align-items: center;
  gap: 3px;
  flex-shrink: 0;
}
.wc-btn {
  width: 15px;
  height: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  margin: 0;
  font-family: inherit;
  font-size: 9px;
  line-height: 1;
  background: #fff2ee;
  border: 1px solid var(--ink);
  border-radius: 2px;
  color: var(--ink);
  box-shadow: inset 1px 1px 0 #fff, inset -1px -1px 0 var(--dusty-mauve);
}
.wc-btn.wc-close { background: var(--cherry-blossom); }
.wc-btn.wc-max {
  width: 22px;
  height: 22px;
  font-size: 13px;
  cursor: pointer;
}
@media (max-width: 400px) {
  .wc-min, .wc-close { display: none; }
}

/* A small folded-corner "page curl" in the top-right, borrowed from
   celestial-sea's layout (a little folded-triangle graphic in its box's top
   corner) - reads clearly as "this is a card/page" without relying on the
   checkerboard pattern that was confusing sitting next to the real
   transparency checkerboard elsewhere. Lives on the titlebar (not the
   module) since the titlebar visually owns the module's top edge. */
.lookbook-titlebar::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 20px 20px 0;
  border-color: transparent var(--panel) transparent transparent;
  filter: drop-shadow(-1px 1px 1px rgba(46,33,36,.4));
  pointer-events: none;
}

.preview-arrow {
  background: var(--panel-2);
  border: 2px solid var(--ink);
  color: var(--text);
  font-size: 1.8rem;
  width: var(--tap);
  height: var(--tap);
  border-radius: 50%;
  flex-shrink: 0;
  cursor: pointer;
  box-shadow: 2px 2px 0 var(--ink);
}
.preview-arrow:active { box-shadow: none; transform: translate(2px, 2px); }

.current-item-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 4px 0 2px;
}
.current-item-name {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.4;
  text-align: center;
  min-height: 1.4em;
  color: var(--accent);
}
.current-item-name::before,
.current-item-name::after { content: "\2605"; color: var(--accent-3); margin: 0 6px; font-size: 0.85em; }
.current-item-name.is-covered { color: var(--text-dim); font-style: italic; font-weight: 400; }

.star-btn {
  background: var(--panel-2);
  border: 2px solid var(--ink);
  border-radius: 50%;
  color: var(--accent-3);
  -webkit-text-stroke: 1px var(--ink);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  width: var(--tap);
  height: var(--tap);
  box-shadow: 2px 2px 0 var(--ink);
  flex-shrink: 0;
}
.star-btn:active { box-shadow: none; transform: translate(2px, 2px); }

.slot-tools-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 8px;
}
.slot-tools-row .btn { flex: 0 1 auto; padding: 0 12px; font-size: 0.88rem; }
.slot-tools-row .btn-local-file { border-style: dashed; }

.action-row.prominent-row {
  gap: 10px;
  margin: 4px 0 14px;
}
.action-row.prominent-row .btn { min-height: 54px; font-size: 1.08rem; }

.slot-chip-row, .chip-filter-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 4px 2px 12px 2px;
  -webkit-overflow-scrolling: touch;
}
.slot-chip-row::-webkit-scrollbar, .chip-filter-row::-webkit-scrollbar { display: none; }

.slot-chip {
  flex-shrink: 0;
  background: var(--panel);
  border: 2px solid var(--ink);
  color: var(--text-dim);
  padding: 10px 16px;
  border-radius: 999px;
  font-size: 1.02rem;
  cursor: pointer;
  min-height: var(--tap);
}
.slot-chip.active { border-color: var(--accent); color: var(--text); background: var(--panel-2); box-shadow: 2px 2px 0 var(--accent); }
.slot-chip.covered { opacity: 0.45; }
.slot-chip .lock-dot { color: var(--accent-2); margin-left: 4px; }

.action-row {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}

.btn {
  flex: 1;
  /* Flex items won't shrink below their own text's natural width by
     default (min-width:auto) - on narrow phones that pushed two side-by-
     side buttons (like Randomize Outfit / Save Outfit) wider than the
     screen instead of letting their labels wrap, so the row overflowed and
     the tail end of the text was only reachable by scrolling sideways.
     min-width:0 plus allowing the label to wrap fixes it at any width. */
  min-width: 0;
  min-height: var(--tap);
  border-radius: var(--radius);
  border: 2px solid var(--ink);
  font-family: var(--font-body);
  font-size: 1.02rem;
  font-weight: 700;
  cursor: pointer;
  padding: 0 10px;
  white-space: normal;
  text-align: center;
  line-height: 1.2;
  box-shadow: 3px 3px 0 var(--ink);
  transition: transform .06s, box-shadow .06s;
}
.btn:active { box-shadow: none; transform: translate(3px, 3px); }
.btn-primary {
  background: repeating-linear-gradient(45deg, var(--accent), var(--accent) 10px, #f7c3cc 10px, #f7c3cc 20px);
  color: var(--ink);
}
.btn-secondary { background: var(--panel-2); color: var(--text); }
.btn-outline { background: var(--panel); border: 2px solid var(--ink); color: var(--text); }
.btn-danger { background: #fbecee; border: 2px solid var(--danger); color: var(--danger); }
.btn.locked { outline: 3px solid var(--accent-2); }
.btn-small { flex: none; padding: 0 14px; min-height: 40px; }

/* A little inconsistency is the point here - these five buttons are
   deliberately built as different little "objects" rather than one
   restyled component, echoing a page where widgets got added over time. */

/* Plain grey Windows-95-style button, for the neutral "None" action */
.btn-win95 {
  flex: 0 0 auto;
  background: #ded7d5;
  border: 2px solid #786866;
  border-radius: 4px;
  color: var(--ink);
  box-shadow: inset 1px 1px 0 #fff, inset -1px -1px 0 #a89490, 2px 2px 0 rgba(46,33,36,0.25);
  min-width: 84px;
}
.btn-win95:active { box-shadow: inset -1px -1px 0 #fff, inset 1px 1px 0 #a89490; transform: translate(1px, 1px); }

/* Shared icon styling for the tiny hand-drawn pixel padlock/die glyphs used
   below - real vector shapes (not emoji, not a bitmap font glyph) built as
   a solid silhouette with a "punched-through" panel-colored cutout, the
   same trick used for the transparency checkerboard's crisp edges. Sized to
   sit inline with the button label. */
.btn-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  shape-rendering: crispEdges;
}

/* Lock and Slot (randomize-this-slot) used to be a hazard-stripe panel and
   a dashed dice-pip button - both read as "ugly"/out of place once the rest
   of the button row calmed down to the new pastel palette, and there was no
   suitable padlock/die image in the Flair folder to reach for instead. Both
   are now the same simple bordered pastel pill as the rest of the toolbar,
   distinguished from each other only by their icon and its accent color -
   dusty-mauve for Lock, apricot-gold for Slot - so they read as a matched
   pair that belongs on the page rather than two mismatched decorations. */
.btn-security, .btn-dice {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--panel);
  color: var(--text);
  border: 2px solid var(--ink);
  border-radius: 10px;
}
.btn-security .btn-icon { color: var(--accent-2); }
.btn-dice .btn-icon { color: var(--accent-3); }
.btn-security.locked {
  background: var(--pastel-pink);
  color: #5a4247;
}

/* Dusty-mauve beveled floppy disk, for saving an outfit */
/* Was a blue-lavender periwinkle before this round's palette swap - moved
   onto the new palette's own dusty-mauve/alabaster family so it doesn't
   sit outside the rest of the page's color language. */
.btn-floppy {
  background: linear-gradient(180deg, var(--alabaster-grey) 0%, #b9c7bd 50%, var(--dusty-mauve) 100%);
  color: var(--ink);
  border: 2px solid #5f4d51;
  border-radius: 4px;
  box-shadow: inset 1px 1px 0 rgba(255,255,255,.8), 3px 3px 0 var(--ink);
  position: relative;
}
.btn-floppy:active { box-shadow: inset 1px 1px 0 rgba(255,255,255,.5); transform: translate(3px, 3px); }

/* ---------- Generic screen bits ---------- */

.screen-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 8px;
}
h2 {
  font-family: var(--font-display);
  font-size: 1.3rem;
  margin: 14px 0 10px 0;
  color: var(--accent);
  -webkit-text-stroke: 0.75px var(--ink);
  display: inline-block;
  position: relative;
}
/* a marker-scribble underline, like it got highlighted in a notebook */
h2::after {
  content: "";
  display: block;
  height: 6px;
  margin-top: -2px;
  background: var(--accent-3);
  opacity: 0.55;
  border-radius: 3px;
  transform: rotate(-1deg) scaleX(1.06);
}
/* A little bordered callout instead of bare text - one more small frame in
   a page that's meant to feel built out of a lot of them. */
.hint {
  color: var(--text-dim);
  font-size: 1rem;
  margin: 0 0 10px 0;
  padding: 7px 11px;
  background: var(--panel-2);
  border: 1px solid var(--text-dim);
  border-left: 4px solid var(--accent-2);
  border-radius: 0 8px 8px 0;
}

.text-input {
  width: 100%;
  min-height: var(--tap);
  background: var(--panel);
  border: 2px solid var(--ink);
  border-radius: 10px;
  color: var(--text);
  padding: 0 12px;
  font-size: 1.08rem;
  margin-bottom: 10px;
  font-family: var(--font-body);
}

.field-label { display: block; font-size: 1rem; color: var(--text-dim); margin-bottom: 4px; }

.item-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
  margin-top: 8px;
}

.item-card {
  background: var(--panel);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  padding: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  position: relative;
  box-shadow: 3px 3px 0 var(--panel-2);
  transition: transform .12s;
}
/* a scrapbook-sticker wobble, alternating tilt/shadow color by position */
.item-card:nth-child(4n+1) { box-shadow: 3px 3px 0 var(--accent-3); }
.item-card:nth-child(4n+2) { box-shadow: 3px 3px 0 var(--accent-2); }
.item-card:nth-child(4n+3) { box-shadow: 3px 3px 0 var(--panel-2); }
.item-card:nth-child(4n+4) { box-shadow: 3px 3px 0 var(--accent-4); }
.item-card:hover, .item-card:focus-visible { transform: rotate(-1.5deg) translateY(-2px); }
.item-card:nth-child(even):hover { transform: rotate(1.5deg) translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
  .item-card { transition: none; }
  .item-card:hover, .item-card:focus-visible { transform: none; }
}
.item-card img, .item-card .no-sprite {
  width: 72px;
  height: 72px;
  object-fit: contain;
  image-rendering: pixelated;
  background-image:
    linear-gradient(45deg, #b8b8c2 25%, transparent 25%),
    linear-gradient(-45deg, #b8b8c2 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #b8b8c2 75%),
    linear-gradient(-45deg, transparent 75%, #b8b8c2 75%);
  background-color: #e8e6ef;
  background-size: 10px 10px;
  background-position: 0 0, 0 5px, 5px -5px, -5px 0;
  border-radius: 8px;
}
.item-card .no-sprite {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.78rem;
  color: var(--text-dim);
  text-align: center;
  padding: 4px;
}
.item-card .name { font-family: var(--font-label); font-size: 0.95rem; text-align: center; font-weight: 700; }
.item-card .status-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  font-size: 0.7rem;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--ink);
  color: var(--text-dim);
}
.item-card .status-badge.approved { color: #2f7a4f; }
.item-card .status-badge.pending { color: #92661a; }
.item-card .status-badge.needs_review { color: #a5472c; }
.item-card .fav-star { position: absolute; top: 6px; left: 6px; font-size: 1rem; color: var(--accent-3); -webkit-text-stroke: 1px var(--ink); }

.tabs { display: flex; gap: 8px; margin-bottom: 8px; }
.tab {
  flex: 1;
  min-height: 44px;
  border-radius: 10px;
  border: 2px solid var(--ink);
  background: var(--panel);
  color: var(--text-dim);
  font-weight: 700;
  font-family: var(--font-body);
}
.tab.active { background: var(--accent-3); color: var(--ink); }

.lock-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.lock-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--panel);
  border: 2px solid var(--ink);
  border-radius: 10px;
  padding: 10px 14px;
  min-height: var(--tap);
}
.lock-row .name { font-family: var(--font-label); font-size: 1rem; }
.lock-row button {
  background: var(--panel-2);
  border: 2px solid var(--ink);
  color: var(--text-dim);
  border-radius: 8px;
  min-height: 40px;
  padding: 0 14px;
  font-family: var(--font-body);
  font-weight: 700;
}
.lock-row button.locked { color: #5a4247; background: var(--pastel-pink); outline: 2px solid var(--accent-2); }

.toggle-row, .slider-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  font-size: 1.02rem;
}
.slider-row input[type=range] { flex: 1; accent-color: var(--accent); }

.photo-preview img { max-width: 140px; border-radius: 10px; margin-bottom: 10px; border: 2px solid var(--ink); }
.request-textarea {
  width: 100%;
  background: var(--panel);
  color: var(--text);
  border: 2px solid var(--ink);
  border-radius: 10px;
  padding: 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
  margin: 10px 0;
}

.review-canvas-row { display: flex; justify-content: center; margin-bottom: 10px; }
.review-canvas-row .canvas-frame canvas { width: 260px; height: 260px; background:#e6e6e6; image-rendering:pixelated; }

.nudge-pad {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  margin: 0 auto 14px auto;
  width: 180px;
}
.nudge-pad .nudge-mid { display: flex; gap: 4px; }
.nudge-pad button {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  border: 2px solid var(--ink);
  background: var(--panel-2);
  color: var(--text);
  font-size: 1.3rem;
  box-shadow: 2px 2px 0 var(--ink);
}
.nudge-pad button:active { box-shadow: none; transform: translate(2px, 2px); }

/* ---------- Bottom nav ---------- */

.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  background: var(--surface-dark);
  padding-bottom: env(safe-area-inset-bottom);
  border-top: 3px solid var(--ink);
}
/* mauve checkerboard ribbon, bookending the apricot one under the header */
.bottom-nav::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -11px;
  height: 11px;
  background-image:
    linear-gradient(45deg, var(--ink) 25%, transparent 25%),
    linear-gradient(-45deg, var(--ink) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--ink) 75%),
    linear-gradient(-45deg, transparent 75%, var(--ink) 75%);
  background-color: var(--accent-2);
  background-size: 11px 11px;
  background-position: 0 0, 0 5.5px, 5.5px -5.5px, -5.5px 0;
}
.nav-btn {
  flex: 1;
  background: none;
  border: none;
  color: #c9b3ae;
  font-size: 1.25rem;
  min-height: 66px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
  font-family: var(--font-body);
  position: relative;
}
.nav-btn img {
  width: 26px;
  height: 26px;
  object-fit: contain;
  image-rendering: pixelated;
}
/* Closet used to be a paw-print Flair GIF, left over from a "cute icon" grab
   bag rather than actually meaning anything for a clothing closet - there
   was no hanger/wardrobe image anywhere in the Flair folder to swap in, so
   this is a small hand-built pixel hanger instead, same technique as the
   Lock/Slot icons. Using currentColor also means it recolors with the tab's
   active state automatically, unlike the static-color GIF icons next to it. */
.nav-icon {
  width: 26px;
  height: 26px;
  shape-rendering: crispEdges;
}
.nav-btn span {
  font-family: var(--font-pixel);
  font-size: 0.6rem;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: inherit;
}
.nav-btn.active { color: var(--accent-3); }
.nav-btn.active span { color: #fff; }
/* Five labeled tabs in a fixed bar get tight on small phones: the Press Start
   2P labels are wide ("Settings" alone is ~92px at the base size), so at ~375px
   all five overflowed the viewport and the last tab clipped off-screen. Below
   480px, shrink the label a step and let each button collapse to an equal 1/5
   share (min-width:0) so they always fit. Roomier desktop size is unaffected. */
@media (max-width: 480px) {
  .nav-btn { min-width: 0; }
  .nav-btn span { font-size: 0.44rem; letter-spacing: 0; }
}
/* glittery marquee underline beneath the active tab */
.nav-btn.active::after {
  content: "";
  position: absolute;
  bottom: 5px;
  left: 18%;
  right: 18%;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-3), var(--accent-2), var(--accent));
  background-size: 300% auto;
  animation: shimmer 2.4s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .nav-btn.active::after { animation: none; }
}

.nav-badge {
  position: absolute;
  top: 4px;
  right: calc(50% - 22px);
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-family: var(--font-pixel);
  font-size: 0.5rem;
  line-height: 15px;
  text-align: center;
  border: 1.5px solid var(--ink);
  display: none;
}
/* .nav-badge is itself a <span>, so the generic ".nav-btn span{color:inherit}"
   rule above (one class + one tag = higher specificity than .nav-badge's own
   single-class color:#fff) was quietly winning and making the badge number
   inherit the nav button's dim, unselected text color instead of staying
   white - only the ".nav-btn.active span" rule happened to also set white,
   which is why it only looked correct on the selected tab. This selector
   is specific enough to always win regardless of active state. */
.nav-btn .nav-badge { color: #fff; }

/* ---------- Fullscreen preview ---------- */

.fullscreen-overlay {
  position: fixed;
  inset: 0;
  /* background-image/size (the sparkle backdrop) come from the shared rule
     near .canvas-frame above - only set background-color here so that
     shorthand doesn't clobber it. */
  background-color: #e6e6e6;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 50;
}
.fullscreen-overlay.open { display: flex; }
#fullscreen-canvas {
  background: #e6e6e6;
  image-rendering: pixelated;
  width: min(90vw, 90vh);
  height: min(90vw, 90vh);
}
.close-fullscreen { position: absolute; top: calc(16px + env(safe-area-inset-top)); right: 16px; }

/* ---------- Toast ---------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: 96px;
  transform: translateX(-50%) translateY(20px);
  background: var(--surface-dark);
  color: #fff;
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 0.95rem;
  font-weight: 700;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 60;
  border: 2px solid var(--accent-3);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

@media (min-width: 720px) {
  main#screens { max-width: 640px; margin: 0 auto; }
  .app-header { max-width: 640px; margin: 0 auto; width: 100%; }
  .bottom-nav { max-width: 640px; margin: 0 auto; left: 0; right: 0; }
}

/* ---------- Mobile: framed app shell (round 19) ----------
   Below the desktop breakpoint, give mobile the same bounded flex-column shell
   the desktop already uses (round 15/16), instead of letting the whole page
   scroll. The masthead and status bar pin to the top, the nav pins to the
   bottom, and ONLY main#screens scrolls in the space between - so the header
   and nav never slide away, the Randomize/Save row is never hidden behind the
   nav (the nav is now a real flex sibling, not a viewport-fixed bar overlapping
   content), and the lookbook stays framed on screen. Leans on the existing
   body{display:flex;flex-direction:column;min-height:100vh} to size the shell
   to the viewport, so no vh/dvh math is needed here. */
@media (max-width: 799px) {
  .site-shell { flex: 1; min-height: 0; display: flex; flex-direction: column; }
  .app-header { flex-shrink: 0; }
  .status-bar { flex-shrink: 0; }
  .site-body { display: flex; flex: 1; min-height: 0; }
  main#screens {
    align-self: stretch;
    display: flex;
    flex-direction: column;
    /* Short screens (most non-Creator screens) center in the available height;
       a tall screen (Creator) falls back to top-aligned + this element's own
       overflow-y:auto, rather than clipping. */
    justify-content: safe center;
    /* The 96px bottom padding was clearance so the OLD viewport-fixed nav
       wouldn't cover the last row - the nav is in-flow now, so that job is
       gone. Back to a small cosmetic gap; reclaims ~76px of scroll. */
    padding-bottom: 16px;
  }
  /* Un-fix the nav: it becomes the bottom item of the shell's flex column, so
     it sits at the real bottom of the screen (still thumb-reachable) with
     content scrolling ABOVE it instead of underneath it. */
  .bottom-nav {
    position: relative;
    left: auto;
    right: auto;
    bottom: auto;
    flex-shrink: 0;
  }
  /* Decorative "est. 2026 · mood" flavor line - dropped on mobile so the nav
     is the true bottom edge and the flavor text doesn't eat scarce height. */
  .site-footer { display: none; }

  /* --- Light compaction so the Creator screen mostly fits the frame without
     scrolling (the framed shell above makes any remaining scroll graceful, but
     less is better). Kept gentle so nothing gets cramped or illegible. --- */
  /* Status pills back to a single tidy row: smaller chips fit all four across
     ~375px, so the round-17 wrap (still the safety net) doesn't trigger and the
     bar reclaims its second row (~40px). */
  .status-bar { gap: 6px; padding: 6px 12px; }
  .stat-chip { font-size: 0.66rem; padding: 2px 7px; }
  /* Trim slack around the module and the prominent action row. */
  .lookbook-module { margin: 6px 0 6px; }
  .action-row.prominent-row { margin: 4px 0 8px; }
}

/* ---------- Real desktop, not just a stretched phone ---------- */
/* Below this width everything above is deliberately mobile-first (the app's
   actual daily-use case). Past it, the editor itself - not just its
   container - scales up ~25%, because a small fixed-size widget adrift in a
   huge field of background reads as "why is this only using a fifth of my
   monitor" rather than as a calm, spacious personal page. The header and
   nav are deliberately left at their normal size - the size gap between a
   modest identity strip and a showcased, enlarged editor is doing real
   work for the "personal homepage" feel and shouldn't be flattened out. */
@media (min-width: 800px) {
  /* The page becomes a genuine bordered "island" on the checkerboard
     wallpaper instead of a small centered widget adrift in empty space. A
     deep patterned backing shows in the shell's own gutters (around the
     masthead and side rails); the checkerboard wallpaper stays outside the
     shell entirely. main#screens keeps its own opaque light background
     regardless, so every screen that isn't wrapped in its own panel
     (Library, Favorites, Randomizer, Settings...) stays exactly as readable
     as before - only the frame around it got darker and busier.
     The stark purple backing from an earlier round is gone. A second pass
     replaced it with a "deep warm mauve-brown" - which in practice still
     read as too dark/muddy against the soft pastel palette everywhere else
     on the page. This is a much lighter dusty-rose derived directly from
     cherry-blossom + dusty-mauve, closer in weight to the rest of the
     page's pastels - it still reads as a distinct "frame" around the
     lighter panels sitting on top of it, just without going dark/gothic. */
  .site-shell {
    max-width: 1100px;
    margin: 0 auto;
    /* Fully flush (0) read as too tight right at the top edge - a few
       pixels of breathing room above the banner, roughly matching the
       visual gap that's already there at the bottom (from main#screens'
       own bottom padding, reserved for bottom-nav clearance). Left/right/
       bottom stay flush. */
    padding: 8px 0 0;
    background-image: repeating-linear-gradient(45deg, #e7c3c8 0 9px, #d9a9b0 9px 18px);
    border: 4px solid var(--ink);
    /* The banner PNG's own corner is a hand-drawn pixel-art curve, not a
       true circular arc - even at a roughly matching nominal radius it
       doesn't overlay this box's CSS border-radius cleanly, leaving a small
       sliver of striped background visible right at the corner. Erring
       smaller (rather than trying to match it exactly) keeps this box's
       clip curve safely inside the image's own already-rounded corner at
       every point, trading a marginally squarer CSS corner for guaranteed
       no gap. */
    border-radius: 10px;
    box-shadow: 0 12px 0 rgba(46,33,36,.15);
    overflow: hidden;
    /* This box was never actually height-bounded - it just grew to fit
       whatever main#screens needed, with nothing to stop it from growing
       past the viewport. overflow-y:auto on main#screens (further down)
       had nothing to bound itself against, so it never engaged; the whole
       shell just kept growing taller than the window with no way to scroll
       to the rest of it (only zooming out shrank things enough to see the
       bottom). Capping height here and turning this into a flex column
       lets main#screens claim exactly "whatever's left after the header
       and status bar," so its own scrollbar can finally do its job. Short
       screens are unaffected - the cap only bites when content is
       genuinely taller than the viewport. */
    display: flex;
    flex-direction: column;
    max-height: 100vh;
  }
  /* Matches the 8px site-shell gives the banner above it (site-shell's own
     top padding) - without this, the checker-strip divider sits flush
     against the status bar below while the banner has visible breathing
     room above, an asymmetry that reads as a mistake once you notice it. */
  /* The masthead-checker is the header's bottom divider (absolute, bottom:0).
     Round 16 put the header's breathing room BELOW it (margin-bottom:8px),
     which left an 8px sliver of the card's striped backing between the checker
     and the dark status bar - it read as an awkward orphaned gap under the
     divider rather than as space for the header. Moved that space ABOVE the
     divider instead: drop the bottom margin so the checker sits flush on the
     status bar, and pad the header's bottom so the banner lifts ~8px clear of
     the checker (the divider now caps the header, with the gap framing the
     banner - mirroring the 8px the shell's top padding gives above it).
     align-items:flex-start keeps that padding as space BELOW the banner only
     (default center would split it above and below). 19px = the 11px checker
     height + the 8px visible gap above it. */
  .app-header { max-width: none; width: 100%; margin: 0; align-items: flex-start; flex-shrink: 0; }
  .status-bar { flex-shrink: 0; }
  /* Round 16 fix: on mobile this is a viewport-fixed bar (see the base
     rule) so it's always thumb-reachable without scrolling away. On
     desktop it's now a normal last item in .site-shell's own flex column
     instead (see the HTML comment by its new markup location) - un-fixing
     it here so it sits exactly where the card's own content ends, at any
     card height, instead of relying on the card growing tall enough to
     coincidentally line up with a viewport-pinned bar. position:relative
     (not static) keeps it as the positioning context its own ::before
     ribbon needs. */
  .bottom-nav {
    position: relative;
    left: auto;
    right: auto;
    bottom: auto;
    max-width: none;
    margin: 0;
    flex-shrink: 0;
  }
  /* This sits right after .bottom-nav in the card's flex column now (round
     16), but it has no background of its own - so the striped site-shell
     backing was showing through behind it, breaking the nav's solid dark
     bar right before the card's own bottom edge instead of extending
     cleanly to it. Giving it the same dark fill as the nav (and no border
     between them) merges the two into one continuous block, so the solid
     color genuinely reaches the bottom of the card - the footer text just
     reads as a quieter last line within that block, the way a copyright
     line often sits inside a dark site footer rather than below it. */
  .site-footer {
    padding: 6px 16px 10px;
    background: var(--surface-dark);
    color: #c9b3ae;
    opacity: 0.75;
  }

  /* The expand-preview button no longer lives in the header (it moved onto
     the lookbook module's own window-controls), so the banner runs the full
     width of the card. The bottom padding lifts the banner ~8px clear of the
     masthead-checker divider (the divider then caps the header flush against
     the status bar). This lives on .masthead rather than .app-header because
     the header carries both classes and this rule would otherwise override an
     .app-header padding shorthand; 19px = 11px checker + 8px visible gap. */
  .masthead { padding: 0 0 19px; }

  /* Bottom padding is 24px (not 4px) so the pink main#screens box clears the
     bottom-nav's checker ribbon (an 11px strip that sits ABOVE the nav via
     ::before): 4px left the ribbon overlapping the box's lower ~9px, cutting off
     its rounded bottom edge. 24px = the 11px ribbon + a ~13px clean gap that
     roughly matches the 14px gap above the box, so the box reads as fully
     enclosed with breathing room before the checkerboard. */
  .site-body { display: flex; align-items: flex-start; gap: 14px; padding: 14px 14px 24px; flex: 1; min-height: 0; }
  .side-rail { display: block; width: 164px; flex-shrink: 0; }

  main#screens {
    flex: 1;
    min-width: 0;
    /* Used to be min-height:calc(100vh - 380px) - an approximate guess at
       "how tall is the space above me" so short screens would still have
       room to vertically center in. site-shell/site-body now propagate a
       real bounded height down (see the site-shell comment above) - but
       .site-body is a ROW (side-rail next to main), and its
       align-items:flex-start (needed so the side-rail keeps its own short
       natural height instead of stretching to match main) also meant main
       itself was opted OUT of being sized to that bounded height, so
       min-height:0 alone had nothing to actually shrink against - main just
       kept its natural content height regardless, same as before. align-self
       overrides that just for main, so it actually receives site-body's
       real height, and min-height:0 can finally do its job letting
       overflow-y:auto (below) engage. */
    align-self: stretch;
    min-height: 0;
    display: flex;
    flex-direction: column;
    /* "safe center" - centers short screens (the Creator) in the available
       height, but falls back to start-aligned + scrollable instead of
       clipping when a screen's content (e.g. a long Library list) is
       taller than the space available. */
    justify-content: safe center;
    /* This used to be doing double duty as both "breathing room under the
       buttons" AND "clearance from the fixed nav" - the second job now
       belongs to .site-shell's max-height (above), which caps the whole
       card so it can never grow into the nav's territory in the first
       place. This is back to being a small, purely cosmetic gap between the
       last button row and the card's own bottom border. */
    padding: 10px 20px 20px;
    max-width: 1040px;
    margin: 0 auto;
    background: var(--bg);
    border: 3px solid var(--ink);
    border-radius: var(--radius);
    box-shadow: 4px 4px 0 rgba(46,33,36,.2);
  }
  .wardrobe-stage { max-width: 760px; grid-template-columns: 100px minmax(0,1fr) 100px; gap: 18px; }
  #preview-canvas { width: 380px; height: 380px; }
  .lookbook-module { padding: 14px 14px 18px; max-width: 860px; margin-left: auto; margin-right: auto; }
  .lookbook-title-group { flex-direction: row; align-items: baseline; gap: 8px; }
  .lookbook-title { font-size: .84rem; }
  .lookbook-status { font-size: 1.05rem; }
  .current-item-name { font-size: 1.15rem; }
  .slot-tile { width: 100px; min-height: 92px; }
  .slot-tile img, .slot-tile-empty { width: 62px; height: 62px; }
  .slot-tile-label { font-size: .8rem; }
  .cycle-btn { width: 56px; min-height: 36px; font-size: 1.5rem; }
  .star-btn { font-size: 1.4rem; }
  .slot-tools-row .btn { font-size: .96rem; padding: 0 14px; }
  .action-row.prominent-row { max-width: 760px; margin: 2px auto 4px; }
  .action-row.prominent-row .btn { min-height: 52px; font-size: 1.1rem; }
  .item-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}

/* Real pointer devices get hover feedback - everything up to this point was
   designed for taps, which meant a mouse got no feedback at all on desktop. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover, .icon-btn:hover, .cycle-btn:hover, .star-btn:hover, .nudge-pad button:hover {
    filter: brightness(1.07);
  }
  .nav-btn:hover { color: var(--accent-3); }
  .nav-btn.active:hover { color: #fff; }
  .slot-tile:hover:not(.active) { border-color: var(--accent-2); transform: translateY(-2px); }
  .item-card:hover { transform: rotate(-1.5deg) translateY(-3px); }
  .slot-chip:hover, .tab:hover, .picker-item:hover { border-color: var(--accent); }
}


/* ---------- Paper-doll creator ---------- */
/* One more nested frame between the lookbook module's own border and the
   avatar's canvas-frame - a dashed inset panel, echoing the dashed "broider"
   box borders picked up from the 32bit.cafe reference a few rounds back. */
.wardrobe-stage {
  display:grid; grid-template-columns:68px minmax(0,1fr) 68px; align-items:center; gap:8px;
  max-width:520px; margin:4px auto 4px;
  border: 2px dashed var(--text-dim);
  border-radius: 12px;
  padding: 8px 6px;
  background: rgba(255,255,255,.45);
}
/* The character column stretches to the full height of the slot rails beside it
   (they're taller than a square preview, so there's blank vertical space in the
   stage). The frame fills that height and the sprite scales to COVER it
   (object-fit below), so the standing figure - which only occupies ~35% of the
   square sprite's width but ~84% of its height - gets much larger by using the
   vertical room and cropping its empty side margins, WITHOUT growing the stage
   (its height is set by the rails, so the card's total height is unchanged and
   the desktop scale-to-fit doesn't shrink it back). */
.character-column { min-width:0; align-self:stretch; }
/* The canvas fills its grid cell rather than sizing itself to the viewport:
   it used to be min(272px,66vw), but 66vw (~248px at 375px) is wider than the
   middle column the 3-track wardrobe-stage grid actually allocates it (~183px
   on a phone), so the sprite overflowed its own frame and overlapped the slot
   rails on both sides. width:100% keeps it inside its cell at every width; the
   frame hugs it, and the rails stay clear. Desktop sets an explicit size in
   the min-width:800 block. */
/* Fill the (now portrait) frame and scale the square sprite to COVER it - the
   figure grows to the frame's height and only the empty left/right sprite
   margins are cropped (the frame is always taller than wide, so cover never
   crops the head or feet). */
#preview-canvas { width:100%; height:100%; object-fit:cover; }
.slot-rail { display:flex; flex-direction:column; gap:7px; }
.slot-tile {
  width:68px; min-height:66px; padding:11px 3px 3px; border:2px solid var(--dusty-mauve); border-radius:11px;
  background:var(--panel); color:var(--text-dim); display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; position:relative; cursor:pointer; overflow:hidden;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.6);
}
/* quiet glossy header strip on every tile, brighter/gradient only when active */
.slot-tile::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 8px;
  background: linear-gradient(180deg, #f7eeee, var(--alabaster-grey));
  border-bottom: 1px solid var(--dusty-mauve);
}
.slot-tile.active {
  border-color: var(--ink);
  background: var(--panel-2);
  color: var(--text);
  box-shadow: 2px 2px 0 var(--accent-2), 4px 4px 0 var(--accent);
}
.slot-tile.active::before {
  background: linear-gradient(180deg, var(--cherry-blossom), var(--accent));
  border-bottom: 1px solid var(--ink);
}
/* tiny pixel-font tag announcing which tile is currently open in the editor */
.slot-tile.active::after {
  content: "NOW EDITING";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 8px;
  font-family: var(--font-pixel);
  font-size: 3.6px;
  letter-spacing: 0.02em;
  line-height: 8px;
  text-align: center;
  color: #fff;
  text-shadow: 0 0 1px #000;
  white-space: nowrap;
  overflow: hidden;
}
/* checkerboard corner accent, bottom-right, only on the active tile */
.slot-tile.active .slot-tile-lock,
.slot-tile.active .slot-tile-review { z-index: 1; }
.slot-tile.active {
  background-image:
    linear-gradient(45deg, var(--accent-3) 25%, transparent 25%),
    linear-gradient(-45deg, var(--accent-3) 25%, transparent 25%);
  background-size: 7px 7px;
  background-position: 100% 100%;
  background-repeat: no-repeat;
}
.slot-tile.covered { opacity:.5; }
.slot-tile img {
  width:43px; height:43px; object-fit:contain; image-rendering:pixelated; border-radius:8px;
  background-image:
    linear-gradient(45deg, #b8b8c2 25%, transparent 25%),
    linear-gradient(-45deg, #b8b8c2 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #b8b8c2 75%),
    linear-gradient(-45deg, transparent 75%, #b8b8c2 75%);
  background-color:#e8e6ef; background-size:8px 8px; background-position:0 0, 0 4px, 4px -4px, -4px 0;
}
.slot-tile-empty { width:43px; height:43px; display:grid; place-items:center; border-radius:8px; background:var(--pastel-pink); border:1px dashed var(--text-dim); font-size:.7rem; font-weight:800; letter-spacing:.04em; }
.slot-tile-label { width:100%; font-family:var(--font-label); font-size:.72rem; line-height:1.3; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-align:center; font-weight:700; }
.slot-tile-lock { position:absolute; top:2px; right:3px; color:#5a4247; font-size:.68rem; font-weight:800; }
.slot-tile-review { position:absolute; top:4px; left:4px; width:7px; height:7px; border-radius:50%; background:#e08a63; box-shadow:0 0 0 2px var(--panel); }
.cycle-row { display:flex; justify-content:center; gap:10px; margin-top:2px; }
.cycle-btn { width:54px; min-height:34px; border:2px solid var(--ink); border-radius:10px; background:var(--panel-2); color:var(--text); font-size:1.4rem; cursor:pointer; box-shadow:2px 2px 0 var(--ink); }
.cycle-btn:active { box-shadow:none; transform:translate(2px,2px); }
.slot-picker { position:fixed; inset:0; z-index:70; display:none; align-items:flex-end; justify-content:center; }
.slot-picker.open { display:flex; }
.slot-picker-backdrop { position:absolute; inset:0; border:0; background:rgba(46,33,36,.55); }
.slot-picker-sheet { position:relative; width:min(640px,100%); max-height:min(72vh,620px); overflow:hidden; background:var(--panel); border-radius:22px 22px 0 0; border:3px solid var(--ink); border-bottom:none; padding:14px 14px calc(18px + env(safe-area-inset-bottom)); box-shadow:0 -8px 0 var(--accent-3); }
.slot-picker-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; }
.slot-picker-header h2 { margin:2px 0 0; }
.slot-picker-eyebrow { color:var(--accent-2); text-transform:uppercase; letter-spacing:.12em; font-size:.75rem; font-weight:800; }
.slot-picker-grid { overflow-y:auto; max-height:calc(min(72vh,620px) - 92px); display:grid; grid-template-columns:repeat(auto-fill,minmax(92px,1fr)); gap:9px; padding:2px; }
.picker-item { min-height:112px; border:2px solid var(--ink); border-radius:13px; background:var(--panel-2); color:var(--text); padding:7px 5px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px; position:relative; cursor:pointer; }
.picker-item.selected { border-color:var(--accent); box-shadow:2px 2px 0 var(--accent); }
.picker-item img {
  width:68px; height:68px; object-fit:contain; image-rendering:pixelated; border-radius:10px;
  background-image:
    linear-gradient(45deg, #b8b8c2 25%, transparent 25%),
    linear-gradient(-45deg, #b8b8c2 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #b8b8c2 75%),
    linear-gradient(-45deg, transparent 75%, #b8b8c2 75%);
  background-color:#e8e6ef; background-size:9px 9px; background-position:0 0, 0 4.5px, 4.5px -4.5px, -4.5px 0;
}
.picker-item-none { width:68px; height:68px; display:grid; place-items:center; border-radius:10px; background:var(--pastel-pink); border:1px dashed var(--text-dim); font-size:.92rem; color:var(--text-dim); }
.picker-item-name { font-family:var(--font-label); font-size:.88rem; line-height:1.25; text-align:center; font-weight:700; }
.picker-item-status { position:absolute; top:5px; right:5px; width:8px; height:8px; border-radius:50%; background:#2f7a4f; }
.picker-item-status.needs_review { background:#e08a63; }
@media (max-width:390px) { main#screens { padding-left:8px; padding-right:8px; } .wardrobe-stage { grid-template-columns:62px minmax(0,1fr) 62px; gap:5px; } .slot-tile { width:62px; min-height:54px; padding:6px 3px 4px; } .slot-tile img,.slot-tile-empty { width:34px; height:34px; } }
@media (min-width:720px) { .wardrobe-stage { grid-template-columns:78px minmax(0,1fr) 78px; gap:14px; } .slot-tile { width:78px; min-height:72px; } }

/* ---------- Desktop de-zoom (round 21) ----------
   The desktop layout read as too zoomed-in: the full-bleed banner alone ate
   ~44% of a 1366x768 viewport, and the Creator screen overflowed its frame by
   ~550px of internal scroll, so the lookbook could never fit at once. The user
   chose to shrink the WHOLE page while keeping the banner edge-to-edge.
   IMPORTANT: this block is deliberately the LAST thing in the file. The base
   paper-doll rules and the min-width:720 block both sit AFTER the main
   min-width:800 block far above, so most of that block's editor sizes were dead
   (overridden by source order - e.g. its 380px canvas and 100px tiles never
   applied; tiles actually computed to the 720 block's 78px). Putting the
   desktop overrides here, last, makes them authoritative. Everything below is
   the single source of truth for desktop editor sizing. */
@media (min-width: 800px) {
  /* Narrower card => the edge-to-edge banner is proportionally shorter (its
     height is just card-width / the banner's fixed aspect ratio), and the whole
     editor column is tighter. This is the main "less zoomed-in" lever. */
  .site-shell { max-width: 840px; }
  .side-rail { width: 145px; }
  /* Non-scaled screens stay in normal flow; centre the card so a short screen
     doesn't anchor to the top with dead wallpaper below it. */
  body { justify-content: center; }
  /* CREATOR screen only: main.js adds .fit-scaled and sets a scale so the whole
     card is scaled as one unit to fit the viewport ("scale X to the Y of the
     screen") - out of flow, pinned to centre, height/scroll caps removed so
     scaling fills the screen with no gaps and nothing clipped. Other screens
     deliberately DON'T scale: a long Library scaled to fit would become
     unreadably tiny, so they keep the normal-flow max-height:100vh + internal
     scroll from the min-width:800 block above. overflow:hidden, display:flex
     column, border, and background all still come from that block. */
  .site-shell.fit-scaled {
    max-width: none;
    width: 840px;
    max-height: none;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%); /* JS appends scale(...) */
    transform-origin: center center;
  }
  /* Decorative footer flavor line hidden on desktop too (already hidden on
     mobile) - reclaims ~37px of height so the Creator screen fits without
     scrolling; the nav is the clean bottom edge. Easy to restore if wanted. */
  .site-footer { display: none; }

  /* Editor scaled down ~20-25% so the whole Creator screen fits a normal
     desktop window instead of needing internal scroll. */
  .lookbook-module { max-width: 560px; padding: 8px 12px 8px; }
  .lookbook-title { font-size: 0.8rem; }
  .lookbook-status { font-size: 0.8rem; }
  /* With the narrower module the inline title+status no longer fit side by
     side (they need ~450px in a ~320px titlebar and were ellipsizing), so
     stack them here the same way mobile does - each gets the full width. This
     overrides the wider-desktop row treatment set in the main min-width:800
     block above. */
  .lookbook-title-group { flex-direction: column; align-items: flex-start; gap: 1px; }
  .current-item-name { font-size: 1rem; }
  .wardrobe-stage { max-width: 440px; grid-template-columns: 70px minmax(0,1fr) 70px; gap: 12px; }
  .slot-rail { gap: 5px; }
  #preview-canvas { max-width: 236px; }
  /* Tile size is comfortable again now that vertical centering (not shrinking)
     handles the layout - the earlier 28px thumbnails were an over-shrink to
     chase fit that made the whole editor read as small. */
  .slot-tile { width: 70px; min-height: 48px; padding: 4px 4px 3px; gap: 1px; }
  .slot-tile img, .slot-tile-empty { width: 34px; height: 34px; }
  .slot-tile-label { font-size: 0.64rem; }
  .cycle-btn { width: 44px; min-height: 28px; font-size: 1.2rem; }
  .star-btn { font-size: 1.1rem; }
  /* Tighten the vertical rhythm of the rows below the character. */
  .current-item-row { margin: 1px 0 0; }
  .cycle-row { margin-top: 2px; }
  .slot-tools-row { margin-top: 2px; }
  /* The action row now lives INSIDE the lookbook window as its footer (see the
     HTML), so it spans the module's full content width like a dialog's action
     bar and gets a small top gap to separate it from the tools row above. The
     window's own bottom padding provides the clean finish below it. */
  .action-row.prominent-row { max-width: none; margin: 8px 0 0; }
  .action-row.prominent-row .btn { min-height: 42px; font-size: 0.98rem; }
}

/* atrament.us fix: outfit card Rename/Delete wrap instead of overflowing narrow cards. */
.outfit-actions { flex-wrap: wrap; justify-content: center; }
.outfit-actions .btn { flex: 1 1 auto; min-width: 0; }

/* Local-only tools (Compare sprites, Copy PNG Path) are marked data-local-only in index.html and shown
   only when the page runs on localhost; hidden on atrament.us (Q18, D42). */
html:not(.local-preview) [data-local-only] { display: none !important; }
