/* =============================================================================
   fx.css: the lit shelf, the toolkit's particles, transitions and empty states.
   Loaded last, so it can refine what the earlier sheets set without editing them.

     1. springs and press feedback
     2. sheets that leave properly, panes that slide
     3. the toolkit layer: particles, rings, flyers, banners
     4. illustrated empty and loading states
     5. the lit shelf (phone grade)

   Motion rules for the whole file: transform and opacity only, nothing loops
   unless a player can see it, reduced motion draws nothing, Light mode draws
   the quiet version.
   ============================================================================= */
:root{
  --fx-spring:cubic-bezier(.34,1.56,.64,1);
  --fx-settle:cubic-bezier(.22,1,.36,1);
  --fx-accent:var(--mint);
}
@supports (transition-timing-function:linear(0,1)){
  :root{--fx-spring:linear(0,.18 6%,.58 14%,.92 22%,1.08 30%,1.1 36%,1.04 44%,.99 54%,1.005 70%,1);--fx-settle:linear(0,.36 7%,.74 17%,.93 29%,1.01 43%,1.005 60%,1)}
}

/* ---- 1. press feedback ------------------------------------------------------
   Everything tappable gives a little under the finger and springs back. The
   individual transform properties compose with the transforms already in use. */
:is(.tab,.chip,.filter-chip,.tray-item,.mh-tile,.mh-choice,.activity-card,.door,.museum-door,.arrival-resident,.mh-shelf-item,.souls-hud,.studio-tab,.opt,.sw,.story-folder>summary):not(:disabled){
  transition-property:scale,background-color,border-color,color,box-shadow,translate,opacity;
  transition-duration:.34s,.14s,.14s,.14s,.14s,.15s,.14s;
  transition-timing-function:var(--fx-spring),ease,ease,ease,ease,ease,ease;
}
:is(.tab,.chip,.filter-chip,.tray-item,.mh-tile,.mh-choice,.activity-card,.door,.museum-door,.arrival-resident,.mh-shelf-item,.souls-hud,.studio-tab,.opt,.sw,.story-folder>summary):not(:disabled):active{
  scale:.965;transition-duration:.06s,.14s,.14s,.14s,.14s,.15s,.14s;
}
.tab:active svg{scale:.9}
.tab svg{transition:scale .3s var(--fx-spring)}
.pet:active .sprite,.prop:active svg{scale:.97}
.btn:active{transition-duration:.06s}
/* a card that opens does it with a little weight */
details[open]>.folder-content,details[open]>.theatre-buttons,details[open]>.gen-tuning,details[open]>.theatre-repertoire{animation:fx-unfold .42s var(--fx-settle) both;transform-origin:50% 0}
@keyframes fx-unfold{from{opacity:0;transform:translateY(-8px) scale(.985)}to{opacity:1;transform:none}}
.note--new{animation:fx-note-in .55s var(--fx-spring) both}
@keyframes fx-note-in{from{opacity:0;transform:translateY(14px) rotate(-1.4deg) scale(.96)}to{opacity:1;transform:none}}

/* ---- 2. sheets that leave properly ------------------------------------------ */
.veil.fx-leaving{display:block;pointer-events:none;animation:fx-veil-out .2s ease-in forwards}
.veil.fx-leaving .sheet{animation:fx-sheet-out .2s cubic-bezier(.5,0,.8,.4) forwards}
@keyframes fx-veil-out{to{opacity:0}}
@keyframes fx-sheet-out{to{opacity:0;transform:translateY(14px) scale(.985)}}
.veil.open .sheet{animation-timing-function:var(--fx-settle);animation-duration:.42s}
@media (max-width:720px){
  .veil.fx-leaving{display:flex}
  .veil.fx-leaving .sheet{animation-name:fx-sheet-down}
  .veil.open .sheet{animation-name:fx-sheet-up;animation-duration:.46s;animation-timing-function:var(--fx-settle)}
  @keyframes fx-sheet-up{from{transform:translateY(72px);opacity:.5}to{transform:none;opacity:1}}
  @keyframes fx-sheet-down{to{transform:translateY(96px);opacity:.4}}
  .pane{animation:fx-pane-in .34s var(--fx-settle) both}
}
/* panes arrive from the side the tab bar says they live on (nav.js sets --pane-dx) */
body[data-tab-dir="forward"] .pane{--pane-dx:26px}
body[data-tab-dir="back"] .pane{--pane-dx:-26px}
@keyframes fx-pane-in{from{opacity:0;transform:translate3d(var(--pane-dx,0px),8px,0)}to{opacity:1;transform:none}}
@media (min-width:721px){.pane{animation:fx-pane-in .3s var(--fx-settle) both}}
body[data-effects="light"] .veil.fx-leaving .sheet{animation-duration:.14s}
body[data-effects="light"] .pane{animation-duration:.18s}
body[data-effects="light"] details[open]>.folder-content{animation:none}

/* ---- 3. the toolkit layer ----------------------------------------------------- */
#fxLayer{position:fixed;inset:0;z-index:95;pointer-events:none;overflow:hidden;contain:strict}
.fx-burst{position:absolute;width:0;height:0}
.fx-p{position:absolute;left:0;top:0;width:calc(var(--s) * 1px);height:calc(var(--s) * 1px);margin:calc(var(--s) * -.5px) 0 0 calc(var(--s) * -.5px);
  color:var(--c);background:currentColor;will-change:transform,opacity;
  animation:fx-fly var(--t) cubic-bezier(.16,.74,.34,1) var(--d) both}
.fx-p.fx-s-star{clip-path:polygon(50% 0,61% 39%,100% 50%,61% 61%,50% 100%,39% 61%,0 50%,39% 39%)}
.fx-p.fx-s-chip{width:calc(var(--s) * .62px);height:calc(var(--s) * 1.1px);border-radius:1px}
.fx-p.fx-s-ember{border-radius:50% 50% 50% 50% / 62% 62% 38% 38%;background:radial-gradient(60% 60% at 50% 66%,#fff 0 18%,currentColor 56%,transparent 100%);box-shadow:0 0 8px 1px color-mix(in srgb,currentColor 55%,transparent)}
.fx-p.fx-s-heart{background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21C4 15 2 11 2 7.5A5 5 0 0 1 12 6a5 5 0 0 1 10 1.5C22 11 20 15 12 21z'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21C4 15 2 11 2 7.5A5 5 0 0 1 12 6a5 5 0 0 1 10 1.5C22 11 20 15 12 21z'/%3E%3C/svg%3E") center/contain no-repeat}
.fx-p.fx-s-puff{border-radius:50%;background:radial-gradient(circle at 38% 34%,color-mix(in srgb,currentColor 70%,#fff),currentColor 60%,transparent 100%);opacity:.9}
body[data-effects="light"] .fx-p.fx-s-ember{box-shadow:none}
@keyframes fx-fly{
  0%{transform:translate(0,0) rotate(0deg) scale(.15);opacity:0}
  9%{opacity:1}
  52%{transform:translate(calc(var(--x) * .8),calc(var(--y) * .8 - var(--lift))) rotate(calc(var(--r) * .6)) scale(1)}
  100%{transform:translate(var(--x),calc(var(--y) + var(--fall))) rotate(var(--r)) scale(.45);opacity:0}
}
.fx-ring{position:absolute;width:calc(var(--w) * 1px);height:calc(var(--w) * 1px);margin:calc(var(--w) * -.5px) 0 0 calc(var(--w) * -.5px);border-radius:50%;
  border:2px solid var(--c);opacity:0;animation:fx-ring .7s cubic-bezier(.2,.7,.3,1) both;will-change:transform,opacity}
@keyframes fx-ring{0%{transform:scale(.6);opacity:.9}100%{transform:scale(1.9);opacity:0}}
.fx-flyer{position:absolute;width:26px;height:26px;margin:-13px 0 0 -13px;color:#F6C768;will-change:transform,opacity;
  filter:drop-shadow(0 0 5px color-mix(in srgb,currentColor 70%,transparent))}
.fx-flyer svg{display:block;width:100%;height:100%}
body[data-effects="light"] .fx-flyer{filter:none}

.fx-banner{position:absolute;left:50%;top:calc(var(--topbar-h, 58px) + var(--sat, 0px) + 16px);width:min(92vw,420px);padding:16px 22px 18px;border-radius:18px;text-align:center;
  background:radial-gradient(120% 140% at 50% 0%,color-mix(in srgb,var(--fx-tone) 24%,transparent),transparent 62%),linear-gradient(180deg,#2a1b2e,#170f1d);
  border:1px solid color-mix(in srgb,var(--fx-tone) 62%,transparent);color:var(--ink);
  box-shadow:0 0 0 1px #0007 inset,0 22px 50px -18px #000,0 0 36px -10px color-mix(in srgb,var(--fx-tone) 55%,transparent);
  transform:translateX(-50%);animation:fx-banner-in .62s var(--fx-spring) both}
.fx-banner.tone-candle{--fx-tone:#F6C768}
.fx-banner.tone-mint{--fx-tone:#7FD8C0}
.fx-banner.tone-blood{--fx-tone:#FF6F7F}
.fx-banner.low{top:auto;bottom:calc(40px + var(--sab, 0px));animation-name:fx-banner-in-low}
.fx-banner.out{animation:fx-banner-out .3s ease-in both}
.fx-banner.low.out{animation-name:fx-banner-out-low}
.fx-banner-kicker{display:block;font:700 11px/1 var(--body);letter-spacing:.2em;text-transform:uppercase;color:var(--fx-tone)}
.fx-banner b{display:block;margin-top:8px;font:400 28px/1.08 var(--display);color:#FFE7B0;text-wrap:balance}
.fx-banner p{margin:8px 0 0;font:21px/1.3 var(--hand);color:var(--ink-2);text-wrap:balance}
@keyframes fx-banner-in{from{opacity:0;transform:translateX(-50%) translateY(-18px) scale(.9)}to{opacity:1;transform:translateX(-50%)}}
@keyframes fx-banner-out{to{opacity:0;transform:translateX(-50%) translateY(-10px) scale(.97)}}
@keyframes fx-banner-in-low{from{opacity:0;transform:translateX(-50%) translateY(18px) scale(.9)}to{opacity:1;transform:translateX(-50%)}}
@keyframes fx-banner-out-low{to{opacity:0;transform:translateX(-50%) translateY(10px) scale(.97)}}
body[data-effects="light"] .fx-banner{box-shadow:0 18px 36px -18px #000}

/* a curio arriving is a small event of its own (celebrate.js marks the reveal) */
.mh-curio-card{position:relative;isolation:isolate}
.mh-curio-card[data-reveal=rare]::after,.mh-curio-card[data-reveal=cursed]::after,.mh-curio-card[data-reveal=unholy]::after{content:'';position:absolute;inset:-30px;z-index:-1;pointer-events:none;border-radius:50%;
  background:radial-gradient(closest-side,color-mix(in srgb,var(--rc,#F6C768) 36%,transparent),transparent);animation:fx-halo 2.4s ease-in-out infinite alternate}
@keyframes fx-halo{from{opacity:.35;transform:scale(.9)}to{opacity:.85;transform:scale(1.06)}}

/* the HUD number counts, so it should not jitter */
.souls-count b,.fx-count{font-variant-numeric:tabular-nums}

@media (prefers-reduced-motion:reduce){
  #fxLayer,.fx-p,.fx-ring,.fx-flyer{display:none}
  .fx-banner{animation:none}
  .mh-curio-card[data-reveal]::after{animation:none}
}

/* ---- 4. illustrated empty and loading states ----------------------------------- */
.fx-empty{display:flex;flex-direction:column;align-items:center;gap:14px;max-width:420px;margin:14px auto;padding:22px 20px 24px;text-align:center;
  border:1px dashed var(--hair-lit);border-radius:var(--r-lg);color:var(--ink-3);
  background:radial-gradient(70% 80% at 50% 0%,color-mix(in srgb,var(--key) 7%,transparent),transparent 70%)}
.fx-empty.compact{flex-direction:row;text-align:left;gap:14px;padding:14px 16px;max-width:none;margin:10px 0}
.fx-empty-art{flex:none;width:min(210px,62%);color:var(--ink-3)}
.fx-empty.compact .fx-empty-art{width:92px}
.fx-empty-art .empty-art{display:block;width:100%;height:auto;overflow:visible}
.fx-empty-copy{display:flex;flex-direction:column;align-items:center;gap:7px;min-width:0}
.fx-empty.compact .fx-empty-copy{align-items:flex-start}
.fx-empty-copy b{font:400 22px/1.15 var(--display);color:var(--ink);text-wrap:balance}
.fx-empty-copy p{font:20px/1.3 var(--hand);color:var(--ink-2);text-wrap:balance}
.fx-empty-action{margin-top:6px}
.empty-art .ea-line{fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.empty-art .ea-line.faint{opacity:.45}
.empty-art .ea-line.dotted{stroke-dasharray:.1 5.2;stroke-width:2.2}
.empty-art .ea-fill{fill:color-mix(in srgb,currentColor 11%,transparent)}
.empty-art .ea-acc{stroke:var(--fx-accent)}
.empty-art .ea-warm{fill:var(--key);stroke:none}
.empty-art .ea-pin{fill:#B8414F;stroke:#000;stroke-opacity:.4;stroke-width:1}
.empty-art .ea-dot{fill:none;stroke:currentColor;stroke-width:2.6;stroke-linecap:round}
.empty-art .ea-moth,.empty-art .ea-dangle,.empty-art .ea-flame,.empty-art .ea-twinkle,.empty-art .ea-knock,.empty-art .ea-bunny{transform-box:fill-box;transform-origin:50% 0}
.empty-art .ea-flame{transform-origin:50% 100%}
.empty-art .ea-twinkle,.empty-art .ea-win{transform-origin:50% 50%}
body:not([data-effects="light"]) .empty-art .ea-dangle{animation:fx-dangle 5.5s ease-in-out infinite}
body:not([data-effects="light"]) .empty-art .ea-flame{animation:fx-flame 1.8s ease-in-out infinite}
body:not([data-effects="light"]) .empty-art .ea-twinkle{animation:fx-twinkle 3.4s ease-in-out infinite}
body:not([data-effects="light"]) .empty-art .ea-moth{animation:fx-moth 4.2s ease-in-out infinite}
body:not([data-effects="light"]) .empty-art .ea-knock{animation:fx-knock 6s ease-in-out infinite}
@keyframes fx-dangle{0%,100%{transform:translateY(0)}50%{transform:translateY(5px)}}
@keyframes fx-flame{0%,100%{transform:scale(1,1) rotate(-2deg)}50%{transform:scale(.92,1.1) rotate(3deg)}}
@keyframes fx-twinkle{0%,100%{opacity:.5;transform:scale(.8) rotate(0)}50%{opacity:1;transform:scale(1.1) rotate(20deg)}}
@keyframes fx-moth{0%,100%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-4px) rotate(3deg)}}
@keyframes fx-knock{0%,88%,100%{transform:rotate(0)}92%{transform:rotate(-14deg)}96%{transform:rotate(8deg)}}
@media (prefers-reduced-motion:reduce){.empty-art *{animation:none!important}}

.fx-loading{display:flex;align-items:center;gap:10px;padding:10px 2px;color:var(--ink-3);font:20px/1.3 var(--hand)}
.fx-loading-dots{display:inline-flex;gap:5px}
.fx-loading-dots i{width:6px;height:6px;border-radius:50%;background:var(--key);opacity:.35;animation:fx-dot 1.2s ease-in-out infinite}
.fx-loading-dots i:nth-child(2){animation-delay:.18s}
.fx-loading-dots i:nth-child(3){animation-delay:.36s}
@keyframes fx-dot{0%,80%,100%{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.15)}}
body[data-effects="light"] .fx-loading-dots i{animation:none;opacity:.7}
@media (prefers-reduced-motion:reduce){.fx-loading-dots i{animation:none;opacity:.7}}

/* ---- 5. the lit shelf, phone grade ---------------------------------------------
   The look in docs/art-direction.md is a key light above and to the left, so
   every creature has a warm edge on that side and throws a shadow down and to
   the right. That used to be a chain of three live drop-shadow filters on every
   animated sprite, which phones could not afford, so Light effects switched the
   lot off and left a flat shelf. It is now drawn from things that cost nothing
   per frame:

     * the rim light is a gradient stroke inside the creature's own SVG
       (art/creatures.js, .cr-rim), retinted here from the room's bulb;
     * the shadow on the wall behind is a pre-blurred ellipse (a radial
       gradient on .sprite::before), the shadow on the board under the feet is
       the existing .pet::after;
     * nothing here animates, so Light and Full look the same and differ only
       in how much moves.

   The filter rules in style.css (the "shadow chain" blocks) are overridden, not
   deleted, so that file stays merge-friendly; they can be removed outright. */
body .sl2 .sprite-figure,
body .pet .sprite-figure,
body .pet:hover .sprite-figure,
body .pet.furious .sprite-figure,
body .pet.asleep .sprite-figure,
body .card-portrait .sprite-figure,
body .gen-mount .sprite-figure,
body .draw-preview .sprite-figure,
body .shelf-theatre-actor .sprite-figure,
body[data-effects="light"] .guest-portrait .sprite{filter:none!important}

/* the rim, retinted */
.sprite-creature .cr-rim{stroke-width:2.5;stroke-linecap:round;transition:opacity .3s}
.sprite-creature .cr-rim-hi{stop-color:color-mix(in srgb,var(--key) 48%,#fff)}
.sprite-creature .cr-rim-a{stop-color:var(--key)}
.pet:hover .sprite-creature .cr-rim,.pet:focus-visible .sprite-creature .cr-rim{stroke-width:3.3}
.pet.furious .sprite-creature .cr-rim-hi{stop-color:#FFA0AA}
.pet.furious .sprite-creature .cr-rim-a{stop-color:#FF4A57}
.pet.asleep .sprite-creature .cr-rim{opacity:.45}
body.night .sprite-creature .cr-rim{opacity:.85}

/* a drawn creature has no vector edge to light; Full mode gives it one cheaply
   (the filter sits on the still image, not on the breathing figure around it) */
body:not([data-effects="light"]) .sl-handdrawn .sprite-body{filter:drop-shadow(-1.5px -1.5px 0 var(--key-hi))}

/* The shadow on the board or the floor under any creature. On the shelf the
   plank already has its own (.pet::after), so this one is for portraits. */
.sprite.sl2::before{content:'';position:absolute;z-index:-1;pointer-events:none;left:50%;bottom:calc(var(--sl-foot-shadow, 5%) * 1);
  width:64%;height:10%;transform:translateX(-50%);border-radius:50%;
  background:radial-gradient(closest-side,rgba(0,0,0,.6),rgba(0,0,0,.25) 58%,transparent 100%)}
/* On the shelf it is a cast shadow on the wall, thrown down and to the right by
   the key, wider than the creature is thick and most of its height. */
.slot .sprite.sl2::before{left:63%;bottom:7%;width:60%;height:70%;transform:translateX(-50%) skewX(-9deg);
  border-radius:48% 52% 46% 54% / 58% 58% 42% 42%;
  background:radial-gradient(closest-side at 46% 56%,rgba(0,0,0,.82),rgba(0,0,0,.52) 52%,rgba(0,0,0,.16) 82%,transparent 100%)}
.pet.asleep .sprite.sl2::before{opacity:.55}
.pet:hover .sprite.sl2::before{opacity:.85}
.shelf-theatre-actor .sprite.sl2::before,.household-player .sprite.sl2::before{left:60%;bottom:6%;width:56%;height:62%;transform:translateX(-50%) skewX(-9deg);border-radius:48% 52% 46% 54% / 58% 58% 42% 42%;
  background:radial-gradient(closest-side at 46% 56%,rgba(0,0,0,.55),rgba(0,0,0,.3) 55%,transparent 100%)}
/* a creature in a bad way glows red behind itself, which is all it takes */
.pet.furious .sprite.sl2::after{content:'';position:absolute;z-index:-2;pointer-events:none;inset:6% -8% 4%;border-radius:50%;
  background:radial-gradient(closest-side,rgba(255,74,87,.26),rgba(255,74,87,.08) 62%,transparent 100%)}

/* props: one static shadow each, kept in Light too (they do not move) */
body[data-effects="light"] .slot .prop svg{filter:drop-shadow(5px 4px 3px rgba(0,0,0,.5))!important}
body[data-effects="light"] .prop[data-prop="candle"] svg,body[data-effects="light"] .prop[data-prop="lantern"] svg,
body[data-effects="light"] .prop[data-prop="lamp"] svg{filter:drop-shadow(0 0 8px var(--key-mid))!important}
body[data-effects="light"] .prop[data-prop="cauldron"] svg{filter:drop-shadow(0 0 8px rgba(143,224,122,.55))!important}

/* The room in Light mode: static haze and dust stay, the flames still flicker
   (a transform on a tiny element is free), and the sheets, which can no longer
   blur what is behind them, get a heavier scrim instead. */
body[data-effects="light"] #haze::after{display:block;animation:none;opacity:.34}
body[data-effects="light"] .candle i{animation:candle-flicker 2.6s steps(7) infinite}
body[data-effects="light"] .candle i::after{animation:none;opacity:.8}
body[data-effects="light"] .veil{background:rgba(4,3,8,.92)}
body[data-effects="light"] .tabbar{background:color-mix(in srgb,var(--night) 95%,transparent)}
@media (prefers-reduced-motion:reduce){body[data-effects="light"] .candle i{animation:none}}

/* where an empty state sits inside the places that already existed */
.notes-empty{padding:0;font-family:var(--body);font-size:15px;color:var(--ink-3)}
.fx-empty .btn{font-family:var(--body)}
.notes-empty .fx-empty{margin:6px auto}
.stories-vacant{display:none}
body.shelf-vacant .stories-vacant{display:block}
.fr-list+.fx-empty,.fr-empty{margin:0}
.fx-empty .fr-empty{color:var(--ink-2);font:19px/1.3 var(--hand)}
.museum-intro+h3~.album .fx-empty,.album .fx-empty{grid-column:1/-1;margin:4px 0}
.mh-cabinet .fx-empty{margin:4px 0 16px}

/* The daily board when nobody has scored, the neighbours cannot be reached or
   the board is still loading: the same two tin cans, drawn here in CSS because
   the arcade owns that markup and its tests pin its text. */
.ar-board:has(>.ar-board-note:only-child){display:flex;align-items:center;gap:12px}
.ar-board:has(>.ar-board-note:only-child)::before{content:'';flex:none;width:68px;height:48px;opacity:.85;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 70'%3E%3Cg fill='%23B8AB9D' fill-opacity='.14' stroke='%23B8AB9D' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 20h28v30a14 6 0 0 1-28 0z'/%3E%3Cellipse cx='22' cy='20' rx='14' ry='6'/%3E%3Cpath d='M84 20h28v30a14 6 0 0 1-28 0z'/%3E%3Cellipse cx='98' cy='20' rx='14' ry='6'/%3E%3C/g%3E%3Cpath d='M36 26c10 3 14 10 20 15M84 26c-9 3-14 8-18 14' fill='none' stroke='%237FD8C0' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M58 8l2.4 6 6 2.4-6 2.4L58 25l-2.4-6.2-6-2.4 6-2.4z' fill='%23F2C083'/%3E%3C/svg%3E") center/contain no-repeat}
.ar-board:has(>.ar-board-note:only-child) .ar-board-note{font:19px/1.3 var(--hand);color:var(--ink-2)}

/* ---- 6. consistency pass --------------------------------------------------------
   The fifteen things that looked most wrong when the main screens were laid side
   by side at 360, 375, 390, 768 and 1440. Each is a small correction made here so
   the earlier sheets stay untouched. */

/* 1. A sheet's title takes programmatic focus when it opens so screen readers
   start in the right place. It is not a control, so it gets no ring: the ring
   hugged the text and ran into the line under it. */
.sheet h2:focus,.sheet h2:focus-visible,.field-guide h2:focus-visible,[role="dialog"] h2[tabindex="-1"]:focus-visible{outline:none}

/* 2. The line under a sheet title sat touching the title. */
.sheet-head h2+.card-meta,.sheet-head h2+p,.sheet-head h2+div{margin-top:5px}

/* 3. The Stories list: the three rows each started their title in a different
   place (the number, the star and the gap all differed). One column, one gap. */
.story-folder>summary,.story-archives .museum-door{gap:18px;padding-inline:7px}
.folder-number,.story-archives .museum-door>span:first-child{flex:none;min-width:34px;text-align:center}
.story-archives .museum-door{align-items:center;min-height:96px;padding-block:16px}
.story-archives .museum-door b{font:400 24px/1.2 var(--display)}
.story-archives .museum-door small{font-size:12px;margin-top:7px}
@media (max-width:720px){.story-archives .museum-door b{font-size:21px}.story-archives .museum-door small{font-size:11px}}

/* 4. The note board toolbar kept a hairline from when it held a heading, so it
   ended in a short stray rule beside the button. */
.notes-tools::after{display:none}
.notes-tools{justify-content:flex-end}

/* 5. The More drawer: one-line and three-line items sat at different heights
   inside their cells. Top-align them all, and give the active toggles a dot
   that is a shape rather than a typed character. */
.tray-item{justify-content:flex-start}
.tray-item[aria-pressed="true"] span::after{content:'';display:inline-block;width:7px;height:7px;margin-left:8px;border-radius:50%;background:var(--key);box-shadow:0 0 8px var(--key-hi);vertical-align:middle}

/* 6. The legal links at the foot of the drawer were 34 px tall. */
.tray-legal a{display:inline-flex;align-items:center;min-height:44px;padding:0 10px}

/* 7. The rank ladder in the cabinet scrolls sideways and the last tile was cut
   off with no sign there was more. A soft edge says so. */
.mh-ladder{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent)}

/* 8. "See it move" hung under the portrait at a different width to it. */
.portrait-stage .motion-preview{width:100%}

/* 9. The playroom's "playing with" bar touched the first game card. */
#playroomVeil .playroom-company{margin-bottom:12px}

/* 10. Buttons inside the empty states and banners keep the body face. */
.fx-banner,.fx-empty{font-family:var(--body)}

/* 11. Light mode: the sheets lose their blur, so give the sheet itself a
   stronger edge instead of letting the dimmed room show through it. */
body[data-effects="light"] .sheet{box-shadow:0 30px 70px -28px #000,inset 0 1px 0 var(--key-soft),0 0 0 1px #0008}

/* ---- 7. the first ten seconds ---------------------------------------------------
   A page that is still loading shows a candle and a line instead of an empty
   frame (after half a second, so a fast load never sees it); the arrival comes
   in one piece at a time; and on a small phone the three residents and the way
   in all fit above the tab bar. */
#cabinet:empty{min-height:240px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px}
#cabinet:empty::before{content:'';width:10px;height:17px;border-radius:50% 50% 45% 45% / 62% 62% 38% 38%;opacity:0;
  background:radial-gradient(60% 60% at 50% 72%,#fff 0 20%,#ffe39a 38%,#ff9d3c 72%,#ff5a2a00 100%);box-shadow:0 0 22px 6px #ffb05055;
  animation:fx-boot .6s ease .5s forwards,candle-flicker 1.4s steps(10) .5s infinite}
#cabinet:empty::after{content:'Waking the creatures.';font:22px var(--hand);color:var(--ink-3);opacity:0;animation:fx-boot .6s ease .5s forwards}
@keyframes fx-boot{to{opacity:1}}

@keyframes fx-rise{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
@keyframes fx-fade{from{opacity:0}to{opacity:1}}
.arrival-copy>.empty-kicker{animation:fx-rise .6s var(--fx-settle) both}
.arrival-copy>h1{animation:fx-rise .7s var(--fx-settle) .06s both}
.arrival-copy>p{animation:fx-rise .7s var(--fx-settle) .12s both}
.arrival-resident{animation:fx-rise .75s var(--fx-settle) both}
.arrival-resident:nth-child(1){animation-delay:.16s}
.arrival-resident:nth-child(2){animation-delay:.26s}
.arrival-resident:nth-child(3){animation-delay:.36s}
.arrival-footer{animation:fx-rise .7s var(--fx-settle) .5s both}
body[data-effects="light"] :is(.arrival-copy>*,.arrival-resident,.arrival-footer){animation-name:fx-fade}
/* the portraits stand in a pool of the candles' light and lift when you reach for one */
.arrival-portrait::before{background:radial-gradient(ellipse at 50% 72%,color-mix(in srgb,var(--key) 26%,transparent),transparent 68%)}
.arrival-resident:nth-child(2) .arrival-portrait::before{background:radial-gradient(ellipse at 50% 72%,color-mix(in srgb,#92c9b4 28%,transparent),transparent 68%)}
.arrival-portrait .sprite{transition:translate .35s var(--fx-spring)}
.arrival-resident:hover .arrival-portrait .sprite,.arrival-resident:focus-visible .arrival-portrait .sprite{translate:0 -6px}
.arrival-resident:active .arrival-portrait .sprite{translate:0 1px}
@media (hover:none){.arrival-resident:hover{background:none;border-color:transparent}.arrival-resident:hover .arrival-action{color:var(--key);background:none;border-color:var(--key-mid)}}
@media (prefers-reduced-motion:reduce){.arrival-copy>*,.arrival-resident,.arrival-footer,#cabinet:empty::before,#cabinet:empty::after{animation:none;opacity:1}}

@media (max-width:720px) and (max-height:700px){
  .arrival-invitation .arrival-copy>p{display:none}
  .arrival-invitation .arrival-copy>h1{font-size:clamp(29px,8.2vw,38px);margin:6px 0 4px}
  .arrival-invitation .arrival-portrait{height:clamp(88px,24vw,122px)}
  .arrival-invitation .arrival-portrait .sprite{--pet-h:clamp(84px,22vw,114px)}
  .arrival-invitation .arrival-resident>b{font-size:18px;margin:6px 0 3px}
  .arrival-invitation .arrival-line{min-height:0;margin-bottom:6px}
  .arrival-invitation .arrival-cast{margin-bottom:8px}
}
/* the three "Meet" buttons share a baseline whatever their resident's line does */
.arrival-resident{height:100%}
.arrival-line{min-height:3.9em;margin-bottom:12px}
.arrival-action{margin-top:auto}

/* A pane slides in from the side, which must never make the page wider. */
.app{overflow-x:clip;overflow-clip-margin:64px}
