/* ============================================================
   Gou's Love — shared site styles
   crimson terminal · sashiko stitching
   ============================================================ */
:root{
  --wine:#7e0023; --wine-deep:#56001a; --wine-deeper:#3a0011;
  --blush:#fde9e6; --blush-dim:rgba(253,233,230,.64); --blush-faint:rgba(253,233,230,.34);
  --stitch:rgba(253,233,230,.5);
  --asanoha:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'72'%20height%3D'72'%20viewBox%3D'0%200%2072%2072'%3E%3Cg%20fill%3D'none'%20stroke%3D'%23fde9e6'%20stroke-opacity%3D'0.62'%20stroke-width%3D'1.3'%20stroke-dasharray%3D'4.6%203.4'%20stroke-linecap%3D'round'%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(0%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(45%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(90%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(135%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(180%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(225%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(270%2036%2036)'%2F%3E%3Cpath%20d%3D'M41%2036%20Q53.05%2030%2067%2036%20Q53.05%2042%2041%2036%20Z'%20transform%3D'rotate(315%2036%2036)'%2F%3E%3Cpath%20d%3D'M0%20-6%20V6%20M-6%200%20H6'%20stroke-dasharray%3D'2.6%202.8'%2F%3E%3C%2Fg%3E%3Ccircle%20cx%3D'36'%20cy%3D'36'%20r%3D'2.1'%20fill%3D'%23fde9e6'%20fill-opacity%3D'0.8200000000000001'%20stroke%3D'none'%2F%3E%3C%2Fsvg%3E");
  --glow:0 0 1px rgba(253,233,230,.9),0 0 7px rgba(253,233,230,.45),0 0 16px rgba(253,233,230,.2);
  /* sashiko stitched diamond tile (blush stitches) */
  --sashiko:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='28' viewBox='0 0 56 28'%3E%3Cg fill='none' stroke='%23fde9e6' stroke-opacity='0.55' stroke-width='1.3' stroke-dasharray='4.5 3.5' stroke-linecap='round'%3E%3Cpath d='M0 14H56'/%3E%3Cpath d='M28 2 44 14 28 26 12 14Z'/%3E%3Cpath d='M28 8V20M20 14H36'/%3E%3C/g%3E%3C/svg%3E");
  /* the sashiko strip, decomposed: diamond motif (with its centre stitch) WITHOUT
     the running line, + the line as its own dashed gradient. Strips layer the two
     with background-repeat:space on the diamonds — space only ever lays down
     COMPLETE tiles (flush at both ends, leftover width becomes spacing between
     motifs), so a diamond can never be cut while the thread runs edge to edge. */
  --sashiko-diamond:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='28' viewBox='0 0 56 28'%3E%3Cg fill='none' stroke='%23fde9e6' stroke-opacity='0.55' stroke-width='1.3' stroke-dasharray='4.5 3.5' stroke-linecap='round'%3E%3Cpath d='M28 2 44 14 28 26 12 14Z'/%3E%3Cpath d='M28 8V20M20 14H36'/%3E%3C/g%3E%3C/svg%3E");
  --sashiko-thread:repeating-linear-gradient(90deg, rgba(253,233,230,.55) 0 4.5px, rgba(0,0,0,0) 4.5px 8px);
  /* single sashiko star medallion (for corner ornaments) */
  --stitch-star:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cg fill='none' stroke='%23fde9e6' stroke-opacity='0.74' stroke-width='1.35' stroke-dasharray='3 2.3' stroke-linecap='round'%3E%3Cpath d='M15 3V27M3 15H27M6.8 6.8 23.2 23.2M23.2 6.8 6.8 23.2'/%3E%3Cpath d='M15 9 21 15 15 21 9 15Z'/%3E%3C/g%3E%3Ccircle cx='15' cy='15' r='1.6' fill='%23fde9e6' fill-opacity='0.9'/%3E%3C/svg%3E");
}
*{box-sizing:border-box;margin:0;padding:0;}
html{scroll-behavior:smooth;}
body{
  background:var(--wine-deeper);
  color:var(--blush);
  font-family:"IBM Plex Mono",monospace;
  min-height:100vh;
}
.screen{
  position:relative;min-height:100vh;width:100%;
  /* clip to the cloth: the scattered .gl-patch elements (injected by transition.js)
     sit near the edges and bleed past them — without this they'd stick out and add
     page scroll. Clipping cuts them at the cloth edge, which reads as patches sewn
     right at the hem. The window goes position:fixed during its morph, so it escapes
     this clip and is unaffected. */
  overflow:hidden;
  padding:clamp(1rem,4vh,3rem) clamp(1rem,4vw,3rem);
  /* corde du roi (corduroy) + old woven cloth, plain alpha layers over the wine ground */
  background:
    repeating-linear-gradient(0deg, rgba(0,0,0,.14) 0 1px, rgba(255,255,255,.03) 1px 2px, rgba(0,0,0,0) 2px 4px),
    repeating-linear-gradient(90deg, rgba(0,0,0,.15) 0 1px, rgba(255,255,255,.04) 1px 2px, rgba(0,0,0,0) 2px 4px),
    repeating-linear-gradient(90deg, rgba(20,0,8,.42) 0px, rgba(20,0,8,.08) 2.4px, rgba(255,225,225,.13) 4.5px, rgba(20,0,8,.08) 6.6px, rgba(20,0,8,.42) 9px),
    radial-gradient(ellipse 95% 85% at 50% 38%, #7d0626 0%, #5e0420 55%, #44001a 100%);
  /* Pin the cloth to the viewport, not the page box. When a gallery makes the page
     taller than the screen, the corduroy ribs + radial glow would otherwise STRETCH
     to fill the whole tall page (deforming the vignette). Fixed attachment paints
     the cloth at viewport size and keeps it there as you scroll — so scrolling down
     just reveals "more corde de roi" with the glow staying put, no stretching. The
     flower patches live in a separate top-anchored band (.gl-patch-field) so they
     scroll away with the page while the cloth underneath continues. */
  background-attachment:fixed;
  display:flex;flex-direction:column;align-items:center;
}
/* (sashiko field removed from the page background — corde du roi + woven cloth only) */

/* ---------------- window chrome (retro, X on the right) ---------------- */
.window{
  position:relative;z-index:20;width:min(1040px,94vw);
  border:1px solid rgba(20,0,8,.6);
  /* dark wine terminal cloth (shared by every page) */
  background:
    repeating-linear-gradient(0deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 3px),
    repeating-linear-gradient(90deg, rgba(0,0,0,.18) 0 1px, rgba(0,0,0,0) 1px 3px),
    linear-gradient(160deg, #4e0019 0%, #3c0014 60%, #2c000f 100%);
  box-shadow:0 16px 34px rgba(15,0,6,.6), 0 3px 8px rgba(15,0,6,.55), inset 0 1px 0 rgba(253,233,230,.10);
}
.titlebar{
  display:flex;align-items:center;gap:.7rem;
  padding:.5rem .55rem .5rem .9rem;
  border-bottom:1px solid rgba(253,233,230,.22);
  background:repeating-linear-gradient(45deg, rgba(253,233,230,.05) 0 2px, transparent 2px 5px), rgba(58,0,17,.6);
}
.titlebar .tt{font-size:12px;letter-spacing:.14em;color:var(--blush);text-transform:lowercase;text-shadow:var(--glow);}
.titlebar .tt b{font-weight:500;}
.titlebar .controls{margin-left:auto;display:flex;gap:5px;}
.titlebar .controls button{
  width:20px;height:17px;display:grid;place-items:center;
  border:1px solid var(--blush-faint);background:rgba(253,233,230,.06);
  color:var(--blush-dim);font-family:"IBM Plex Mono",monospace;font-size:11px;line-height:1;cursor:pointer;
  transition:background .18s,color .18s,border-color .18s;
}
.titlebar .controls button:hover{background:rgba(253,233,230,.16);color:var(--blush);border-color:var(--blush);}
.titlebar .controls button.x:hover{background:var(--blush);color:var(--wine-deep);}

/* stitched inner frame (sashiko) */
.win-body{position:relative;padding:clamp(1.3rem,3.2vw,2.4rem);}
.win-body::before{content:"";position:absolute;inset:9px;border:1.4px dashed rgba(253,233,230,.28);pointer-events:none;}

/* ---------------- address / nav bar (old browser) ---------------- */
.addressbar{
  display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;
  padding:.45rem .8rem;border-bottom:1px solid rgba(253,233,230,.16);
  background:rgba(58,0,17,.4);font-size:11px;letter-spacing:.06em;color:var(--blush-dim);
}
.addressbar .url{display:flex;align-items:center;gap:.45rem;flex:1;min-width:160px;
  background:rgba(0,0,0,.22);border:1px solid rgba(253,233,230,.16);padding:.3rem .6rem;color:var(--blush);}
.addressbar .url .frontend-ico{color:var(--blush-faint);}
.addressbar .crumb a{color:var(--blush-dim);}
.addressbar .crumb a:hover{color:var(--blush);}

/* ---------------- nav row ---------------- */
.nav{display:flex;flex-wrap:wrap;gap:.4rem .2rem;justify-content:center;
  padding:.7rem .5rem;font-size:11.5px;letter-spacing:.18em;text-transform:uppercase;border-bottom:1px solid rgba(253,233,230,.14);}
.nav a{color:var(--blush-dim);text-decoration:none;padding:.3rem .8rem;border:1px solid transparent;transition:all .18s;}
.nav a::before{content:"❀ ";color:var(--blush-faint);}
.nav a:hover{color:var(--blush);border-color:rgba(253,233,230,.3);text-shadow:var(--glow);}
.nav a.here{color:var(--wine-deep);background:var(--blush);}
.nav a.here::before{color:var(--wine);}

/* ---------------- typography ---------------- */
.eyebrow{font-size:11px;letter-spacing:.5em;text-transform:uppercase;color:var(--blush-dim);text-shadow:0 0 8px rgba(253,233,230,.25);}
.script{font-family:"Pinyon Script",cursive;font-size:clamp(24px,3.4vw,42px);color:var(--blush);line-height:1;margin-bottom:.05em;text-shadow:var(--glow);}
/* min-height reserves one line (line-height is .96) so the heading keeps its
   height even while the animation backspaces it to empty — the terminal doesn't
   collapse/resize when the title is momentarily blank. */
.bigtitle{font-family:"IM Fell English SC",serif;font-weight:400;font-size:clamp(44px,7vw,92px);line-height:.96;min-height:.96em;letter-spacing:.02em;color:var(--blush);text-shadow:var(--glow);}
.bigtitle .a{font-style:italic;font-family:"IM Fell English",serif;}
.lede{font-family:"IM Fell English",serif;font-style:italic;font-size:clamp(16px,1.9vw,22px);line-height:1.66;max-width:48ch;color:var(--blush);text-shadow:0 0 7px rgba(253,233,230,.26);}
/* ---------------- ascii corner animation ----------------
   The thematic ASCII film (ascii-anim.js) floated into the empty space
   right of the title block — placed before the eyebrow so the heading
   and description wrap AROUND it rather than running underneath on a
   narrow column. Sized to span the eyebrow + title + lede together
   (~200px on desktop): the sets have different row counts (heart 5,
   polaroid 8…), so each room gets its own font-size via [data-anim] to
   land at the same overall height. Frames are pre-padded to one fixed
   box, so the float never resizes mid-loop. Hidden where the space
   beside the title doesn't exist. */
.ascii-anim{float:right;margin:.2rem clamp(1.5rem,5vw,5rem) 1rem 2rem;
  font-family:"IBM Plex Mono",monospace;font-size:clamp(14px,2.2vw,28px);line-height:1.18;
  white-space:pre;color:var(--blush-dim);text-shadow:0 0 8px rgba(253,233,230,.25);
  pointer-events:none;user-select:none;}
.ascii-anim[data-anim="home"]{font-size:clamp(16px,2.6vw,34px);}          /* 5 rows */
.ascii-anim[data-anim="photography"]{font-size:clamp(10px,1.7vw,21px);}   /* 8 rows */
.ascii-anim[data-anim="mixed-media"]{font-size:clamp(12px,1.9vw,24px);}   /* 7 rows */
@media (max-width:860px){.ascii-anim{display:none;}}
.cur{display:inline-block;width:.5ch;height:1.05em;background:var(--blush);vertical-align:-.16em;margin-left:.05em;box-shadow:var(--glow);animation:blink 1.05s step-end infinite;}

/* ---------------- sashiko divider ----------------
   Two layers (see --sashiko-diamond/--sashiko-thread): whole diamonds laid by
   repeat:space — never cut, one flush at each end — over a continuous thread. */
.sashiko{height:28px;width:100%;
  background-image:var(--sashiko-diamond),var(--sashiko-thread);
  background-repeat:space no-repeat,no-repeat;
  background-size:56px 28px,100% 1.3px;
  background-position:center,center;
  opacity:.8;margin:clamp(1rem,2.6vw,1.8rem) 0;}
.sashiko.tight{margin:.6rem 0;}

/* sashiko stitched divider with a centered medallion (frontend + content pages).
   The pattern and the character work TOGETHER: the running stitch splits into
   two threads ending cleanly at the centre, where the same dashed stitching
   curls into a small round hoop holding the hedera (❧) — the leaf is sewn into
   the seam, not pasted over it. Diamonds laid by repeat:space over a continuous
   thread — never cut mid-motif, one flush at each end of each thread. */
.sashiko-divider{height:30px;width:min(440px,74%);margin:.15rem 0;
  display:flex;align-items:center;justify-content:center;gap:.7rem;}
.sashiko-divider::before,.sashiko-divider::after{content:"";flex:1 1 0;height:24px;
  background-image:var(--sashiko-diamond),var(--sashiko-thread);
  background-repeat:space no-repeat,no-repeat;
  background-size:48px 24px,100% 1.3px;
  background-position:center,center;
  opacity:.88;}
.sashiko-divider .med{flex:0 0 auto;display:grid;place-items:center;width:30px;height:30px;
  border-radius:50%;border:1.4px dashed var(--stitch);
  color:var(--blush);text-shadow:var(--glow);font-size:14px;line-height:1;
  filter:drop-shadow(0 1px 1px rgba(15,0,6,.45));}

/* sashiko star ornaments pinned to the terminal's inner-frame corners.
   Applied on every page (the .stitched-corners class on .win-body): make the
   inner running-stitch frame read more deliberately + seat a sashiko medallion
   at each corner of it. */
.win-body.stitched-corners::before{border-color:rgba(253,233,230,.36);}
.win-body.stitched-corners::after{content:"";position:absolute;inset:6px;pointer-events:none;
  background-image:var(--stitch-star),var(--stitch-star),var(--stitch-star),var(--stitch-star);
  background-repeat:no-repeat;
  background-position:left top,right top,left bottom,right bottom;
  background-size:30px 30px;}

/* ---------------- gallery ---------------- */
/* Fixed 230px cells (never flex) laid left-to-right, wrapping with the leftover
   gutter on the right — an old file-manager / icon-view grid, not a modern fluid
   one. grid-auto-rows:1fr keeps every row the same height so captions line up.
   The fixed cell also gives the gallery→viewer morph a deterministic source rect
   (same shape at every viewport width). */
.gallery{display:grid;grid-template-columns:repeat(auto-fill,300px);justify-content:start;grid-auto-rows:1fr;gap:clamp(1rem,2.4vw,1.7rem);}
/* Only place a tile isn't fixed: a viewport too narrow to hold one 300px cell
   inside the window — let the single column shrink so it can't overflow. */
@media (max-width:380px){ .gallery{grid-template-columns:minmax(0,1fr);} }
/* an empty room: no plates yet — a quiet stitched note instead of a bare wall */
.gallery.gallery-empty{display:flex;justify-content:center;}
.empty-note{max-width:46ch;margin:clamp(1rem,3vw,2rem) auto;
  padding:clamp(1.4rem,3vw,2.2rem) clamp(1.2rem,2.6vw,2rem);
  border:1px dashed rgba(253,233,230,.3);background:rgba(58,0,17,.25);text-align:center;}
.empty-note .orn{font-size:20px;color:var(--blush-dim);text-shadow:var(--glow);margin-bottom:.6rem;}
.empty-note p{font-family:"IM Fell English",serif;font-style:italic;font-size:clamp(15px,1.6vw,18px);
  line-height:1.7;color:var(--blush-dim);}

.art{display:flex;flex-direction:column;border:1px solid rgba(253,233,230,.22);background:rgba(58,0,17,.34);}
.art .cap-bar{display:flex;align-items:center;justify-content:space-between;
  padding:.35rem .55rem;border-bottom:1px solid rgba(253,233,230,.16);
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--blush-faint);background:rgba(58,0,17,.5);}
.art .cap-bar .x{color:var(--blush-faint);}
.art .cap-bar a.open{color:var(--blush-dim);text-decoration:none;letter-spacing:.12em;transition:color .18s,text-shadow .18s;}
.art .cap-bar a.open:hover{color:var(--blush);text-shadow:var(--glow);}
/* 4:3 (old-monitor ratio) — the SAME box the viewer hero uses, so the gl-art
   morph is a clean uniform scale, not a stretch between two different shapes. */
.art image-slot{width:100%;aspect-ratio:4/3;height:auto;display:block;}
/* fit=contain letterboxes the art with no crop (Victorian framing); the bars
   should be the wine cloth behind, not the slot's default light frame. */
.art image-slot::part(frame),.view-art image-slot::part(frame){background:transparent;}
/* the whole image tile is a link into the viewer — only when it actually holds a
   picture (the anchor carries [href]); empty placeholder slots stay inert. On
   hover: a soft blush ring + a "view" badge so it reads as clickable. */
.art .plate-link{display:block;position:relative;z-index:6;text-decoration:none;-webkit-user-drag:none;}
.art .plate-link[href]{cursor:pointer;}
.art .plate-link[href]::before{content:"";position:absolute;inset:0;z-index:7;pointer-events:none;
  box-shadow:inset 0 0 0 1px rgba(253,233,230,0),inset 0 0 26px rgba(253,233,230,0);transition:box-shadow .25s ease;}
.art .plate-link[href]:hover::before{box-shadow:inset 0 0 0 1px rgba(253,233,230,.5),inset 0 0 30px rgba(253,233,230,.14);}
.art .plate-link[href]::after{content:"view \2197";position:absolute;right:.5rem;bottom:.5rem;z-index:8;pointer-events:none;
  font-family:"IBM Plex Mono",monospace;font-size:9px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--blush);text-shadow:var(--glow);background:rgba(15,0,6,.72);border:1px solid rgba(253,233,230,.32);
  padding:.24rem .5rem;opacity:0;transform:translateY(4px);transition:opacity .22s ease,transform .22s ease;}
.art .plate-link[href]:hover::after{opacity:1;transform:none;}
.art .meta{padding:.6rem .7rem .7rem;}
/* Fixed single-line title: one line keeps the caption compact (more of the image
   shows) and constant-height — the box never grows or shrinks as the caption types
   in character-by-character (room→room diff), and every card stays the same height.
   The line is vertically centred within the box (height == line-height) but stays
   left-aligned horizontally; an over-long name is clipped with an ellipsis rather
   than wrapping. The cap-bar, the image slot and the flex/nowrap medium line are all
   constant-height too, so no tile is left shorter than its neighbours. */
.art .meta .t{font-family:"IM Fell English",serif;font-style:italic;font-size:16px;color:var(--blush);
  display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  height:2em;line-height:2em;}
a.t{text-decoration:none;display:inline-block;transition:text-shadow .18s;}
a.t:hover{text-shadow:var(--glow);}
.art .meta .m{font-size:10.5px;letter-spacing:.1em;color:var(--blush-dim);margin-top:.25rem;text-transform:uppercase;}

/* ---------------- footer ---------------- */
.foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.8rem;
  margin-top:1.4rem;padding-top:1rem;border-top:1px dashed rgba(253,233,230,.24);
  font-size:10.5px;letter-spacing:.12em;color:var(--blush-faint);text-transform:uppercase;}
.foot a{color:var(--blush-dim);}
.foot a:hover{color:var(--blush);}
.counter{font-family:"IBM Plex Mono",monospace;color:var(--blush-dim);border:1px solid rgba(253,233,230,.2);padding:.2rem .5rem;background:rgba(0,0,0,.25);letter-spacing:.22em;}

a{color:var(--blush);}

/* ---------------- CRT overlays ---------------- */
.scan{position:fixed;inset:0;pointer-events:none;z-index:9;background:repeating-linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,0) 2px,rgba(30,0,10,.20) 3px,rgba(30,0,10,.20) 3.7px);mix-blend-mode:multiply;}
.vig{position:fixed;inset:0;pointer-events:none;z-index:62;background:radial-gradient(ellipse 110% 100% at 50% 50%,rgba(0,0,0,0) 58%,rgba(30,0,10,.5) 100%);box-shadow:inset 0 0 150px rgba(20,0,8,.72);opacity:.85;}
.flick{position:fixed;inset:0;pointer-events:none;z-index:63;background:rgba(253,233,230,.02);animation:flicker 7s infinite;}

/* ---------------- the fissure (secret backend door) ----------------
   A tear in the cloth behind the terminal, injected by transition.js on every page
   that has the window. It's a hole into the black (the void path), ringed by a
   wine-lit torn lip and a faint red seep, with frayed sashiko threads still bridging
   the rip. Centred on the MIDDLE OF THE SCREEN (left:50% top:50vh) — the frontend terminal's
   centre — and the SAME size on every page (height set inline by transition.js: 0.6× the
   frontend terminal). position:absolute, so top:50vh is a document anchor: it scrolls away
   with the page, it doesn't track the viewport. z5: over the cloth + flower patches (z1),
   under the window (z20) — so the terminal hides it: a blind secret.
   Clicking it sinks the page into black (.tear-veil) and slips through to backend.html. */
.fissure{position:absolute;z-index:5;left:50%;top:50vh;transform:translate(-50%,-50%);
  width:clamp(260px,32vw,440px);cursor:pointer;line-height:0;outline:none;
  -webkit-tap-highlight-color:transparent;filter:drop-shadow(0 0 8px rgba(0,0,0,.55));
  transition:filter .4s ease;}
.fissure .fissure-svg{width:100%;height:100%;display:block;overflow:visible;}
.fissure .void{fill:#000;stroke:rgba(12,0,5,.95);stroke-width:1.4;stroke-linejoin:round;vector-effect:non-scaling-stroke;}
.fissure .lip{fill:none;stroke:rgba(126,0,35,.5);stroke-width:2.4;stroke-linejoin:round;mix-blend-mode:screen;vector-effect:non-scaling-stroke;}
.fissure .seep{fill:rgba(150,12,42,.18);animation:fzbreath 6s ease-in-out infinite;}
.fissure .thread{fill:none;stroke:#fde9e6;stroke-linecap:round;vector-effect:non-scaling-stroke;}
/* hover/focus brightens the rip and the seep — but the tear itself does NOT scale */
.fissure:hover,.fissure:focus-visible{filter:drop-shadow(0 0 5px rgba(0,0,0,.6)) drop-shadow(0 0 18px rgba(178,14,46,.6));}
.fissure:hover .seep,.fissure:focus-visible .seep{fill:rgba(176,16,50,.34);}
@keyframes fzbreath{0%,100%{opacity:.7;}50%{opacity:1;}}

/* clicking the tear pulls the whole page into the rip's black before navigating */
.tear-veil{position:fixed;inset:0;z-index:200;background:#000;opacity:0;pointer-events:none;transition:opacity .55s ease;}
html.gl-tear-in .tear-veil{opacity:1;}
@media (prefers-reduced-motion:reduce){.fissure .seep{animation:none;}}

/* ---------------- page transitions ---------------- */
/* Cross-document View Transitions: navigating between these same-origin pages
   morphs the terminal window (gl-window) from one page into the next — the
   frontend terminal expands into a gallery, a gallery settles into the viewer —
   while the wine background + CRT overlays cross-fade as the root. No veil,
   no loading bar. Browsers without the API fall back to a normal instant
   load (still no veil), and transition.js handles the per-page build-in. */
@view-transition { navigation: auto; }

/* The one element that persists across the navigation: the terminal window.
   Exactly one .window per page, so the name stays unique at capture time. */
.window{ view-transition-name: gl-window; }

/* Drive the window morph + its content cross-fade on the same easing the
   first-load winIn uses, so the motion feels like one continuous gesture. */
::view-transition-group(gl-window){
  animation-duration:1.1s;
  animation-timing-function:cubic-bezier(.2,.72,.2,1);
}
::view-transition-old(root),::view-transition-new(root){
  animation-duration:.8s;
  animation-timing-function:ease;
}

/* ---- "dive into the painting" (gallery ↔ artwork viewer) ----
   A CROSS-BROWSER custom zoom — NOT a native View Transition, so it plays in every
   browser online (Chrome, Firefox, Safari), unlike the Chromium-only ::view-transition
   path. The clicked painting is rasterized to ONE flat bitmap; that single texture is
   moved/scaled by a CSS transform (cheap, like a carousel slide) from the tile up
   through a canonical OVERSHOOT and down into the hero, while the page behind (.screen)
   recedes + fades. The overlay is built by dive-boot.js (pre-paint, both browsers) and
   animated by diveOut()/diveIn() in transition.js. The native VT is suppressed for this
   hop via skipTransition(); frontend↔gallery (no gl-diving) keeps the window morph above.

   The overshoot BOX is sized by JS the SAME way on both pages (overshootBox():
   W = min(max(1.5*vw, 2*vh), 2600); H = 0.75*W — a 4:3 box ~1.5× the viewport's larger
   side, capped so a hi-dpi 4K display can't push the layer past the ~8192px GPU texture
   limit), so the leaving and arriving pages draw the IDENTICAL meeting frame — that is
   what makes the document swap seamless. We never scale a live subtree past 1.4×. */
.gl-dive-overlay{position:fixed;inset:0;z-index:120;pointer-events:none;overflow:hidden;}
.gl-dive-overlay .gl-dive-bg{position:absolute;inset:0;background:#f3e9d8;}  /* cream — the meeting colour */
.gl-dive-overlay .gl-dive-img{
  position:absolute;left:0;top:0;transform-origin:0 0;   /* width/height + transform are set inline by JS */
  object-fit:contain;display:block;will-change:transform;}

/* ---------------- in-document 3D dive stage (gallery ↔ artwork viewer) ----------------
   A self-contained perspective overlay above everything (z 130 — over the CRT overlays
   and the legacy z-120 dive). transition.js MOVES the live gallery .screen into
   .gl-layer.gl-term and flies it in Z with a clip-path hole; the freshly rendered viewer
   rises in .gl-layer.gl-world; the flying painting (.gl-fly) sits at z2 between them. The
   wine/cream backdrops fill any edge a Z-shrunk page leaves, so no wrong-colour border.
   Ported from dive-prototype.html — this is the live effect, NOT the .gl-dive-* flat
   zoom above (which is now unused).
   NOTE: the layer classes are gl-term/gl-world (NOT bare term/world) — the site already
   has a .term card style (max-width:520px) that would otherwise clamp the flying wall. */
.gl-stage{position:fixed;inset:0;z-index:130;overflow:hidden;perspective:var(--gl-persp,650px);perspective-origin:50% 45%;}
.gl-stagebg{position:absolute;inset:0;z-index:0;pointer-events:none;}
.gl-stagebg.cream{background:#f3e9d8;}
.gl-stagebg.wine,.gl-term-bg{background:
  repeating-linear-gradient(0deg, rgba(0,0,0,.14) 0 1px, rgba(255,255,255,.03) 1px 2px, rgba(0,0,0,0) 2px 4px),
  repeating-linear-gradient(90deg, rgba(0,0,0,.15) 0 1px, rgba(255,255,255,.04) 1px 2px, rgba(0,0,0,0) 2px 4px),
  repeating-linear-gradient(90deg, rgba(20,0,8,.42) 0px, rgba(20,0,8,.08) 2.4px, rgba(255,225,225,.13) 4.5px, rgba(20,0,8,.08) 6.6px, rgba(20,0,8,.42) 9px),
  radial-gradient(ellipse 95% 85% at 50% 38%, #7d0626 0%, #5e0420 55%, #44001a 100%);}
.gl-term-bg{position:absolute;inset:0;z-index:0;}
.gl-layer{position:absolute;inset:0;transform-style:preserve-3d;backface-visibility:hidden;will-change:transform,clip-path,filter;}
.gl-layer.gl-world{z-index:1;}
.gl-layer.gl-term{z-index:3;}
/* screens placed inside the stage are at rest — suppress the per-page entrance fades */
.gl-layer .screen,.gl-layer .screen > .window{animation:none !important;}
/* The live gallery .screen paints its cloth with background-attachment:fixed (pinned to
   the viewport so it doesn't stretch on tall pages). Once the screen is MOVED into a
   3D-transformed .gl-layer (translateZ), a fixed-attachment background is clamped to a
   viewport-sized sub-region instead of following the geometry — so as the terminal flies
   toward the camera its cloth gets cut off at a hard rectangle. Fix it the way the
   prototype did: the gallery is ALWAYS the terminal layer, which always carries a
   full-bleed .gl-term-bg plane that lives INSIDE the 3D layer and therefore scales and
   clips with the geometry. So drop the gallery screen's own cloth and let that plane paint
   the wine. The cream viewer is always the world layer and keeps its own background, but
   re-anchored to scroll so it likewise travels with the layer in 3D. */
.gl-layer .screen:not(.artview):not(.readview){background:transparent !important;}
.gl-layer .screen.artview,.gl-layer .screen.readview{background-attachment:scroll !important;}
.gl-fly{position:fixed;z-index:2;overflow:hidden;pointer-events:none;will-change:transform;}
.gl-fly img{width:100%;height:100%;object-fit:contain;display:block;}
/* The retro CRT edge-shadow (the global .vig) sits at z62, below the stage (z130), and is
   hidden with the other CRT overlays during the dive — so the monitor vignette would pop
   away the instant a dive starts. Re-create it ON the stage, above the 3D layers, so the
   glass edge stays continuous through the whole transition. It only darkens the viewport
   rim (radial transparent in the centre), so it never reintroduces scanlines in the hole. */
.gl-vig{position:absolute;inset:0;z-index:4;pointer-events:none;
  background:radial-gradient(ellipse 110% 100% at 50% 50%,rgba(0,0,0,0) 58%,rgba(30,0,10,.5) 100%);
  box-shadow:inset 0 0 150px rgba(20,0,8,.72);opacity:.85;}
/* Beveled rim framing the hole — gives the flat terminal apparent THICKNESS. The inset
   shadows read as the recessed inner wall of a thick panel; the transparent centre keeps
   the world visible through the hole. Sits at z3 (with the terminal) and flies with it. */
.gl-rim{position:absolute;z-index:3;pointer-events:none;background:transparent;
  /* An OPAQUE bevelled bezel framing the hole — a solid ring of the wall's own wine, lit
     top/left and shadowed bottom/right, so it reads as a physical thick edge regardless of
     what shows through behind it. border-width is animated 0 → full by transition.js; with
     box-sizing:border-box that grows the bezel INWARD from the edge toward the centre, so
     the wall thickness is revealed as the photo passes through (not full at the start). */
  border-style:solid;border-width:0;
  border-color:#74183a #170410 #110308 #4c1124;     /* lit top · shadow right · darkest bottom · mid left */
  box-shadow:
    inset 0 0 0 1px rgba(253,233,230,.30),          /* bright inner lip (near cut edge) */
    0 0 0 1px rgba(4,0,2,.9);                       /* crisp outer seam on the front face */
}

.screen{animation:screenIn .5s ease both;}
.screen > .window{animation:winIn .62s cubic-bezier(.2,.72,.2,1) both;}
@keyframes screenIn{from{opacity:0;}to{opacity:1;}}
@keyframes winIn{from{opacity:0;transform:scale(.972) translateY(10px);}to{opacity:1;transform:none;}}

/* Arrived via a view transition? The window is already morphing, so skip the
   per-page winIn/screenIn entrance to avoid a double animation. transition.js
   adds .vt-nav to <html> in the pagereveal handler, before first paint. */
.vt-nav .screen,
.vt-nav .screen > .window{animation:none;}

/* component build-in (toggled by transition.js on gallery pages) */
.rise{opacity:0;transform:translateY(22px);}
.rise.in{opacity:1;transform:none;transition:opacity .66s ease,transform .72s cubic-bezier(.2,.75,.25,1);}

@media (prefers-reduced-motion: reduce){
  .screen,.screen > .window{animation:none;}
  .rise{opacity:1;transform:none;}
  /* Make any view transition instant rather than animated. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){ animation:none !important; }
}

@keyframes blink{50%{opacity:0;}}
@keyframes flicker{0%,100%{opacity:.5;}3%{opacity:.7;}6%{opacity:.45;}7%{opacity:.85;}8%{opacity:.5;}50%{opacity:.55;}52%{opacity:.4;}}
@keyframes shake{0%,100%{transform:translateX(0);}20%{transform:translateX(-6px);}40%{transform:translateX(6px);}60%{transform:translateX(-4px);}80%{transform:translateX(4px);}}
.shake{animation:shake .4s;}

/* ============================================================
   ADDITIONS — duality build-out
   woven field · stitched chrome · embroidery hoops ·
   cross-stitch heart · terminal · boot · sew-loader · window ctl
   ============================================================ */

/* ---- (5) real sashiko / woven textile field behind everything ---- */
.screen::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:var(--asanoha);background-size:72px 72px;
  background-attachment:fixed;   /* stay glued to the viewport like the cloth above, so the woven field doesn't drift on a tall page */
  opacity:.07;mix-blend-mode:screen;
}

/* ---- (5b) embroidered patches sewn onto the cloth (every page) ----
   Rose patches scattered around the edges of the wine cloth — like patches sewn onto
   a garment, each at its own slight angle. CSS can't rotate background layers
   individually, so transition.js injects one .gl-patch element per patch and sets
   its position / size / rotation inline (translate(-50%,-50%) places the patch on
   its centre point; the rotation is the "noise"). Drop-shadow gives each a raised,
   stitched-on feel. z-index:1 keeps them on the cloth, above the woven field but
   behind the terminal window (z-index:20), so they fill the margins around the
   centred window without ever covering it — on frontend, galleries, viewer and reader. */
/* The flowers are anchored to a viewport-tall band pinned at the very top of the
   page (not spread over the full document). transition.js injects this field and
   places each .gl-patch inside it with a % position, so the percentages resolve
   against ONE screenful — the composition stays exactly as composed regardless of
   how tall a gallery makes the page. Because the field sits in the page flow, it
   scrolls away as you go down (the flowers "stay where they were") while the
   fixed-attachment cloth underneath keeps going. height matches .screen's min so
   the band lines up with the first screenful. */
.gl-patch-field{position:absolute;left:0;top:0;width:100%;height:100vh;z-index:1;pointer-events:none;}
.gl-patch{
  position:absolute;z-index:1;pointer-events:none;
  background:url("patch_pink_flower.png") center/contain no-repeat;
  opacity:.85;
}
/* per-flower whip-stitch border — drawn as an SVG child by transition.js (and the
   background composer) when a flower's stitches are enabled, so each looks sewn onto
   the cloth. Inherits the rose's rotation since it's nested inside .gl-patch. */
.gl-patch .gl-stitch{position:absolute;inset:0;width:100%;height:100%;overflow:visible;}
.gl-patch .gl-stitch line{stroke:#fbe4df;stroke-width:2.6;stroke-linecap:round;opacity:.95;
  filter:drop-shadow(0 1px 1px rgba(15,0,6,.6));}

/* ---- (6) stitched UI chrome — running stitch instead of plain rules ---- */
.titlebar{border-bottom:1.4px dashed rgba(253,233,230,.42);box-shadow:0 1px 0 rgba(15,0,6,.4);}
.titlebar .controls button{border:1.3px dashed rgba(253,233,230,.42);}
.addressbar .url{border:1px dashed rgba(253,233,230,.32);}
.addressbar{border-bottom:1.2px dashed rgba(253,233,230,.22);}
.nav{border-bottom:1.2px dashed rgba(253,233,230,.2);}
.nav a:hover{border-style:dashed;}
.win-body::before{border-color:rgba(253,233,230,.34);filter:drop-shadow(0 1px 0 rgba(15,0,6,.5));}
.view-art .plate{border-bottom:1.3px dashed rgba(253,233,230,.34);}
.art .cap-bar{border-bottom:1.2px dashed rgba(253,233,230,.2);}

/* ---- (7) cross-stitch heart on the frontend screen ---- */
.cs-wrap{display:flex;flex-direction:column;align-items:center;gap:.55rem;margin-bottom:.1rem;}
.cs-wrap cross-stitch{display:block;line-height:0;}
.cs-toggle{display:flex;border:1px solid rgba(253,233,230,.2);}
.cs-toggle button{appearance:none;background:transparent;border:0;color:var(--blush-faint);
  font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  padding:.32rem .75rem;cursor:pointer;transition:color .2s,background .2s;}
.cs-toggle button + button{border-left:1px solid rgba(253,233,230,.2);}
.cs-toggle button.on{color:var(--wine-deep);background:var(--blush);}
.cs-toggle button:not(.on):hover{color:var(--blush);}

/* ---- (2) terminal prompt (frontend) ---- */
.term{width:100%;max-width:520px;margin:.5rem auto 0;text-align:left;cursor:text;
  border:1px solid rgba(253,233,230,.18);background:rgba(0,0,0,.26);
  font-family:"IBM Plex Mono",monospace;font-size:12px;color:var(--blush-dim);}
.term::before{content:"~/heart — type \2018help\2019";display:block;padding:.32rem .6rem;
  font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--blush-faint);
  border-bottom:1px dashed rgba(253,233,230,.18);}
.term-log{max-height:120px;overflow:auto;padding:.5rem .6rem .25rem;display:flex;flex-direction:column;gap:.2rem;}
.term-log .tl{line-height:1.5;word-break:break-word;}
.term-log .tl b{color:var(--blush);font-weight:500;}
.term-log .echo{color:var(--blush);}
.term-log .dim{color:var(--blush-faint);}
.term-log .err{color:#ffb9a8;}
.term-log .heart{color:var(--blush);text-shadow:var(--glow);}
.term-log .f{color:var(--blush);}
.term-row{display:flex;align-items:center;gap:.4rem;padding:.4rem .6rem .55rem;position:relative;
  border-top:1px dashed rgba(253,233,230,.12);}
.term-row .ps{color:var(--blush);}
.term-row .ps2{color:var(--blush-faint);margin-right:.05rem;}
.term-typed{color:var(--blush);white-space:pre;}
.tcur{display:inline-block;width:.5ch;height:1.05em;background:var(--blush);vertical-align:-.16em;
  box-shadow:var(--glow);animation:blink 1.05s step-end infinite;}
/* The terminal's caret carries the page's blinking cue (the frontend enter
   button gave its block up for it) — it blinks whether or not the terminal is
   focused, just a touch dimmer at rest so focus still reads. */
.term:not(.focus) .tcur{opacity:.55;}
.term-field{position:absolute;left:0;top:0;width:1px;height:1px;opacity:0;border:0;padding:0;margin:0;}

/* ---- frontend reveal (gated by the boot sequence) ---- */
.patch-wrap{transition:opacity .7s ease, transform .8s cubic-bezier(.2,.72,.2,1);}
body.booting .patch-wrap{opacity:0;transform:translateY(14px) scale(.985);}
.patch-wrap.reveal{opacity:1;transform:none;}

/* ---- (1) boot sequence overlay ---- */
.boot{position:fixed;inset:0;z-index:90;display:flex;align-items:center;justify-content:center;cursor:pointer;
  color:var(--blush);font-family:"IBM Plex Mono",monospace;
  background:radial-gradient(ellipse 100% 90% at 50% 42%, #5a0420, #360012 60%, #240010);
  animation:bootIn .3s ease both;}
.boot.out{animation:bootOut .46s ease forwards;}
.boot::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(to bottom,transparent 0 2px,rgba(30,0,10,.22) 3px 3.7px);}
.boot-inner{width:min(560px,86vw);position:relative;z-index:1;}
.boot-pre{margin:0;white-space:pre-wrap;letter-spacing:.02em;color:var(--blush);text-shadow:var(--glow);
  font-size:clamp(11px,1.5vw,14px);line-height:1.85;}
.boot-hint{margin-top:1.1rem;min-height:1.2em;display:flex;align-items:center;gap:.5rem;
  font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--blush-dim);}
.boot-hint .bk{width:8px;height:14px;background:var(--blush);box-shadow:var(--glow);animation:blink 1.05s step-end infinite;}
@keyframes bootIn{from{opacity:0;}to{opacity:1;}}
@keyframes bootOut{to{opacity:0;transform:scale(1.02);}}

/* ---- (8) page transitions are now cross-document View Transitions ----
   The old "sew-the-seam" veil + animated loading bar were removed in favour
   of the window morph defined in the page-transitions section above. */

/* ---- (3) window controls — minimize / maximize / taskbar ---- */
.window{transition:width .5s cubic-bezier(.45,0,.15,1), transform .42s ease, opacity .42s ease, box-shadow .45s cubic-bezier(.2,.72,.2,1);}
/* The frontend terminal is a patch sewn flat into the cloth: sewn, it sits FLUSH —
   no drop shadow, only a faint top highlight (galleries are separate windows
   and keep the seated shadow from .window above). Once the last stitch lets go
   it floats up off the fabric — the shadow swells, drops and softens. On return
   it lands lifted, then settles flush again as boot.js's sewIn() stitches it
   down. The lift is the shadow alone.
   Both states list the SAME three shadows in the same order (drop, drop, inset)
   so the box-shadow can interpolate — the sewn state's two drops are just
   transparent and zero-sized, so they fade and grow IN rather than snapping.
   (A box-shadow transition is discrete if the lists' inset flags don't line up
   position-by-position.) */
.window.frontend-win{box-shadow:0 0 0 rgba(15,0,6,0), 0 0 0 rgba(15,0,6,0), inset 0 1px 0 rgba(253,233,230,.10);}
/* Lifted, it carries the SAME shadow a gallery window has (see .window line 46),
   so the shadow doesn't change across the morph — frontend lifts to the gallery
   shadow then becomes the gallery, and on return arrives wearing it. */
.window.frontend-win.unsewn{box-shadow:0 16px 34px rgba(15,0,6,.6), 0 3px 8px rgba(15,0,6,.55), inset 0 1px 0 rgba(253,233,230,.10);}
/* MORPH ARRIVAL (returning from a gallery): the patch lands wearing the gallery
   shadow, then settles flush as the seam stitches it down. That arrival shadow
   must be on the VERY FIRST painted frame, or it flickers — the old code added
   it via the JS `.unsewn` class at end-of-body, which the browser can paint
   *after* a first frame in the flush (shadowless) rest state, so the shadow read
   as disappear → fade-in → fade-out. .vt-nav is set by frontend.html's pre-paint
   <head> script, so keying the shadow off it guarantees it's there frame one.
   sewIn() then adds .sewn to ease it to flush — the single intended fade. */
html.vt-nav .window.frontend-win{box-shadow:0 16px 34px rgba(15,0,6,.6), 0 3px 8px rgba(15,0,6,.55), inset 0 1px 0 rgba(253,233,230,.10);}
html.vt-nav .window.frontend-win.sewn{box-shadow:0 0 0 rgba(15,0,6,0), 0 0 0 rgba(15,0,6,0), inset 0 1px 0 rgba(253,233,230,.10);}
/* The float-up (a slight raise) is applied only when LEAVING — pulling the patch
   out of the cloth. A RETURNING patch must arrive matching where the gallery
   contracted to (its resting spot), so it carries the shadow but no lift, or the
   terminal jumps up from the morph's endpoint for a frame as the page loads. */
.window.frontend-win.lifted{transform:translateY(-12px);}
.window:not(.frontend-win).win-max{width:97vw!important;}
.patch-wrap:has(.win-max){width:min(1280px,94vw);}
.window.win-min{opacity:0;transform:scale(.55) translateY(60px);transform-origin:bottom left;pointer-events:none;}
.window.win-hidden{display:none;}
.taskbar{position:fixed;left:18px;bottom:16px;z-index:70;display:flex;gap:.5rem;}
.taskbar .chip{display:flex;align-items:center;gap:.5rem;cursor:pointer;animation:chipIn .3s ease both;
  font-family:"IBM Plex Mono",monospace;font-size:11px;letter-spacing:.06em;color:var(--blush);
  background:linear-gradient(160deg,#4e0019,#2c000f);border:1px solid rgba(253,233,230,.28);
  padding:.45rem .8rem;box-shadow:0 8px 18px rgba(15,0,6,.5);transition:background .18s,transform .18s;}
.taskbar .chip:hover{background:linear-gradient(160deg,#5e0420,#3a0014);transform:translateY(-1px);}
.taskbar .chip .g{color:var(--blush-dim);}
@keyframes chipIn{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}

@media (prefers-reduced-motion: reduce){
  .boot{animation:none;}
}

/* ============================================================
   ADDITIONS — round two (trimmed)
   kept: woven-warp scan (Tweaks toggle) · care-label captions · sampler
   ============================================================ */

/* ---- full-resolution archival viewer ----
   The terminal IS the CRT: scanlines now live INSIDE each window. The
   surrounding desktop keeps the global .scan (lowered to sit behind the
   window). Each artwork is then lifted ABOVE the window's scanlines, so the
   work itself is shown at full resolution — true colour, no screen-door. */
.window::after{content:"";position:absolute;inset:0;z-index:4;pointer-events:none;
  background:repeating-linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,0) 2px,rgba(30,0,10,.20) 3px,rgba(30,0,10,.20) 3.7px);
  mix-blend-mode:multiply;}
/* The artwork panel is a SOLID, ISOLATED surface mounted proud of the CRT glass:
   opaque frame + opaque label bar + opaque image, all isolated from the screen's
   scanlines, baked-in window grid, and blend modes. Nothing bleeds through it, so
   the work reads at full resolution. No z-index gamble — the panel is opaque. */
.art,.view-art{position:relative;z-index:5;isolation:isolate;
  background:linear-gradient(160deg,#2a0010,#1c0009 70%,#15000a)!important;}
.art .cap-bar,.view-art .plate{position:relative;z-index:6;
  background:#260010!important;}
/* The slot is a fixed 4:3 box; art is shown `contain` (true ratio, never cropped),
   so any leftover is letterbox bars. Those bars are the wine corduroy cloth (same
   weave as the terminal) with a thin blush mat hairline — the artwork reads as a
   piece mounted on the cloth, identical in the gallery tile and the viewer hero. */
.art image-slot,.view-art image-slot{position:relative;z-index:6;
  background:
    repeating-linear-gradient(0deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 3px),
    repeating-linear-gradient(90deg, rgba(0,0,0,.18) 0 1px, rgba(0,0,0,0) 1px 3px),
    linear-gradient(160deg,#4e0019 0%,#3c0014 60%,#2c000f 100%);
  box-shadow:inset 0 0 0 1px rgba(253,233,230,.16), inset 0 2px 10px rgba(15,0,6,.4);}
/* the focused work wears a small “true colour” badge */
.view-art image-slot::after{content:"\25C8 true colour \00b7 full-res";
  position:absolute;left:0;bottom:0;z-index:7;pointer-events:none;
  font-family:"IBM Plex Mono",monospace;font-size:8.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--blush-dim);background:rgba(15,0,6,.62);padding:.22rem .45rem;
  border-top:1px solid rgba(253,233,230,.2);border-right:1px solid rgba(253,233,230,.2);}
/* ---- fabric care-label gallery captions (opaque, on the panel surface) ---- */
/* The caption box grows to fill the card's remaining height (flex:1) so the dashed
   care-label always reaches the bottom of the tile — never a gap below it when the
   medium line is only one line. Content is centred vertically within that space. */
.art .meta{position:relative;z-index:6;margin:.6rem .55rem .55rem;padding:.55rem .65rem .6rem;
  background:rgba(253,233,230,.045);border:1px dashed rgba(253,233,230,.3);
  flex:1 1 auto;display:flex;flex-direction:column;justify-content:center;}
.art .meta::before{content:"";position:absolute;top:-7px;left:50%;transform:translateX(-50%);
  width:11px;height:11px;border-radius:50%;border:1px solid rgba(253,233,230,.45);
  background:rgba(58,0,17,.7);box-shadow:inset 0 0 0 1.5px rgba(15,0,6,.5);}
.art .meta .m{display:flex;align-items:center;gap:.4rem;}
.art .meta .m::before{content:"\25C7";font-size:8px;color:var(--blush-faint);}
.art .meta .m::after{content:"\00b7 handmade \00b7 keep warm";color:var(--blush-faint);margin-left:auto;
  font-size:9px;letter-spacing:.08em;}

/* ---- terminal: sampler (pre) ---- */
.term-log .pre{white-space:pre;font-size:10px;line-height:1.2;color:var(--blush);
  text-shadow:0 0 5px rgba(253,233,230,.25);}

/* ============================================================
   SIDEBAR SHELL — the room layout (home + the four rooms)
   A wider window with a STICKY RIGHT SIDEBAR: navigation on top, a
   working terminal (shell.js) filling the rest. The menu stays pinned
   in view as the content scrolls beside it. Frontend keeps its sewn
   patch terminal; the cream viewer and the reader keep their own
   worlds — this shell is the chrome of the rooms.
   ============================================================ */

/* a WIDER computer screen to make room for the menu column — 80% of the page,
   uncapped, so the room fills the cloth on any monitor. On a small screen
   (≤760px, where the sidebar stacks below) it widens to 100% so the cramped
   layout reclaims the margins — see the media query above. */
.window.shell-win{width:80vw;}

/* Free the sticky sidebar below. .screen clips with overflow:hidden (to cut the
   scattered flower patches at the cloth edge) — but an overflow:hidden ancestor
   becomes the sticky element's scroll container, and .screen never scrolls
   internally (it grows to fit the window), so the sidebar would ride up with the
   page instead of pinning. Let shell screens overflow normally so the viewport is
   the sidebar's scroll container, and instead clip the patches at the patch-field
   itself (not a sidebar ancestor, so clipping it doesn't re-break sticky) — same
   visual result (patches cut at the band edge) with no page overflow. */
.screen.shell{overflow:visible;}
/* Clip the flowers at the page's left/right hem only. overflow:hidden here cut
   the bottom-band flowers in half at the field's 100vh edge on a long page —
   they should hang below the band onto the cloth like everywhere else. */
.shell .gl-patch-field{overflow-x:clip;overflow-y:visible;}

/* Cloth wear — the imperfections that make the cloth visibly OURS and visibly
   moving when scrolled: two soft press-fold bands recurring down the bolt
   (fixed tile heights, so they never stretch) and a fine slub-noise sprinkle.
   Scrolls with the page like the patches. Sits under them (z0 vs z1). */
.cloth-wear{position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.85;
  background-image:
    linear-gradient(104deg, rgba(0,0,0,0) 30%, rgba(8,0,4,.16) 42%, rgba(255,234,228,.06) 45%, rgba(0,0,0,0) 58%),
    linear-gradient(83deg, rgba(0,0,0,0) 55%, rgba(8,0,4,.13) 68%, rgba(255,234,228,.05) 71%, rgba(0,0,0,0) 82%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.99 0 0 0 0 0.91 0 0 0 0 0.89 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:100% 1450px, 100% 1100px, 240px 240px;
  background-repeat:repeat-y, repeat-y, repeat;}

/* the window body becomes a two-column layout: content + sidebar.
   row-reverse places the menu column on the RIGHT while keeping it first in
   the DOM (so the mobile stack below still drops nav+terminal on top). */
.shell-body{display:flex;align-items:stretch;flex-direction:row-reverse;}

/* ---- the right menu column ----
   Sticky + viewport-tall: the menu (nav + terminal) stays pinned in view as you
   scroll the content beside it, instead of scrolling away. Pinning it to one
   screenful also bounds the terminal — it fills the leftover height below the nav
   rather than stretching down the whole (long) page. align-self:flex-start stops
   it inheriting the content column's full height; the top offset matches
   .screen's padding so it lines up with the window's top edge. */
.sidebar{flex:0 0 clamp(244px,26%,330px);min-width:0;display:flex;flex-direction:column;
  align-self:flex-start;position:sticky;top:clamp(1rem,4vh,3rem);
  height:calc(100vh - 2 * clamp(1rem,4vh,3rem));
  /* a QUILTED pocket: deeper wine cloth carrying the asanoha sashiko pattern —
     unmistakably a different piece of fabric than the window's plain weave.
     Raised off the window: lit fold on its left edge + a soft cast shadow. */
  background:rgba(40,0,16,.5);
  border-left:0;
  box-shadow:inset 1px 0 0 rgba(253,233,230,.14), -1.5px 0 0 rgba(15,0,6,.6), -7px 0 14px rgba(15,0,6,.38);}
/* the quilting, as an overlay so its strength is tunable; under the content */
.sidebar::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:var(--asanoha);background-size:72px 72px;opacity:.13;mix-blend-mode:screen;}
/* nav + terminal sit above the quilting; navigation up top takes only the height
   it needs, the terminal section starts right after and fills the rest */
.side-sec{position:relative;z-index:1;min-height:0;display:flex;flex-direction:column;}
.side-nav{flex:0 0 auto;}
.side-term-sec{flex:1 1 auto;}
.side-sec + .side-sec{border-top:1.5px dashed rgba(253,233,230,.3);}
.side-head{display:flex;align-items:center;gap:.5rem;
  padding:.6rem .85rem;font-size:9.5px;letter-spacing:.26em;text-transform:uppercase;
  color:var(--blush-faint);border-bottom:1px dashed rgba(253,233,230,.18);
  background:repeating-linear-gradient(45deg, rgba(253,233,230,.04) 0 2px, transparent 2px 5px), rgba(58,0,17,.4);}
.side-head::before{content:"❀";color:var(--blush-faint);}
.side-head .hint{margin-left:auto;color:var(--blush-faint);opacity:.8;letter-spacing:.14em;text-transform:none;font-size:9px;}

/* ---- vertical navigation (top of the menu) ----
   Links as embroidered tags: hovering draws a running-stitch underline in
   (thread pulled through, dash by dash) instead of a border popping on; the
   current page is a woven label stitched to the pocket. */
.vnav{display:flex;flex-direction:column;gap:.32rem;padding:.7rem;overflow:auto;}
.vnav a{display:block;position:relative;color:var(--blush-dim);text-decoration:none;
  padding:.55rem .85rem;
  font-size:12px;letter-spacing:.18em;text-transform:uppercase;transition:color .18s,text-shadow .18s;}
.vnav a::before{content:"❀ ";color:var(--blush-faint);}
.vnav a::after{content:"";position:absolute;left:.85rem;right:.85rem;bottom:.28rem;height:2px;
  background-image:repeating-linear-gradient(90deg, #fbe4df 0 6px, rgba(0,0,0,0) 6px 11px);
  background-repeat:no-repeat;background-size:0% 2px;opacity:.85;
  filter:drop-shadow(0 1px 1px rgba(15,0,6,.5));
  transition:background-size .35s ease;}
.vnav a:hover{color:var(--blush);text-shadow:var(--glow);}
.vnav a:hover::after{background-size:100% 2px;}
.vnav a.here{color:var(--wine-deep);background:var(--blush);
  outline:1px dashed rgba(126,0,35,.55);outline-offset:-4px;   /* stitched into the label */
  box-shadow:0 1px 3px rgba(15,0,6,.45);}
.vnav a.here::before{color:var(--wine);}
.vnav a.here::after{display:none;}

/* ---- terminal (fills the rest of the menu) — full-bleed inside its section ----
   The whole terminal scrolls as ONE column: the log lines flow top-down and
   the input prompt follows directly after the last line (like a real shell) —
   near the top when empty, drifting down as output accumulates — instead of the
   prompt being pinned to the bottom of the box. */
.sidebar .term{max-width:none;width:100%;margin:0;flex:1 1 auto;min-height:0;
  border:0;background:rgba(0,0,0,.34);display:block;overflow:auto;}
.sidebar .term::before{display:none;}                 /* the side-head is the label now */
.sidebar .term-log{max-height:none;overflow:visible;flex:0 0 auto;}

/* ---- the content column ---- */
.shell-main{flex:1 1 0;min-width:0;}
.shell-main .win-body{height:100%;}

/* Once the window narrows, the 80% margins start to bite before the layout
   actually breaks — widen the room to the full window early (≤1100px) so the
   content keeps room while there's still a sidebar beside it. Kept separate from
   the 760px stacking breakpoint below so the two can move independently. */
@media (max-width:1100px){
  .window.shell-win{width:100vw;}
}

/* On a narrow screen the menu can't sit beside the content — stack it on top,
   nav then terminal, so the room is still legible on a phone. */
@media (max-width:760px){
  .shell-body{flex-direction:column;}
  /* stacked on top of the content — drop the sticky pin and the viewport height so
     it flows normally above the page rather than holding a full screen */
  .sidebar{flex:0 0 auto;position:static;height:auto;border-left:0;
    border-bottom:1.5px dashed rgba(253,233,230,.3);}
  .side-sec{flex:0 0 auto;}
  .sidebar .term-log{max-height:160px;}
  .vnav{flex-direction:row;flex-wrap:wrap;}
}

/* ============================================================
   RETRO PASS — dithered chrome · odometer counter · pixel cursor
   ============================================================ */
:root{
  /* ordered-ish dither dot, tiled over chrome to read as limited colour depth */
  --dither:radial-gradient(rgba(8,0,4,.22) .5px, transparent .7px);
}

/* ---- pixel cursors (every page) ---- */
body{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath d='M3 2 L3 19 L8 14 L11 21 L14 20 L11 13 L18 13 Z' fill='%23fde9e6' stroke='%233a0011' stroke-width='1.4' stroke-linejoin='round'/%3E%3C/svg%3E") 3 2, auto;}
a,button,.vnav a{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath d='M9 3 L9 12 L7 10 L5 12 L9 21 L17 21 L19 12 L13 11 L13 3 Z' fill='%23fde9e6' stroke='%233a0011' stroke-width='1.4' stroke-linejoin='round'/%3E%3C/svg%3E") 8 2, pointer;}
.term-field{cursor:text;}
html.gl-wait,html.gl-wait *{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cg fill='%23fde9e6' stroke='%233a0011' stroke-width='1.3' stroke-linejoin='round'%3E%3Crect x='6' y='2' width='12' height='2'/%3E%3Crect x='6' y='20' width='12' height='2'/%3E%3Cpath d='M7 4 L17 4 L12 12 Z'/%3E%3Cpath d='M12 12 L17 20 L7 20 Z'/%3E%3C/g%3E%3C/svg%3E") 12 12, wait !important;}

/* ---- dithered chrome fill (titlebar, shell pages) ---- */
.shell .titlebar{background:var(--dither) 0 0/3px 3px, repeating-linear-gradient(45deg, rgba(253,233,230,.05) 0 2px, transparent 2px 5px), rgba(58,0,17,.6);}

/* ---- odometer visitor counter ---- */
.shell .counter{font-family:"IBM Plex Mono",monospace;font-size:12px;letter-spacing:.06em;border:0;background:none;padding:0;}
.shell .counter .od{display:inline-block;background:#0a0206;border:1px solid rgba(253,233,230,.3);
  color:#9ef7b0;text-shadow:0 0 6px #6ef29a;padding:.05rem .26rem;margin:0 1px;border-radius:2px;}

/* ---- the flying manuscript plate (writing list → cream reader dive) ----
   The writing dives through the cloth like the artworks do; the continuous
   element is the burgundy ENTRY PLATE itself — box, title and kind·year fly
   through the slit and land as the reader's .read-plate, while the excerpt
   (the text under the title) is the only thing that fades. Box rect, padding
   and title size are interpolated by transition.js. It rides INSIDE the dive
   stage at z2 — between the world (z1) and the flying terminal wall (z3) — so
   the wall occludes it and it's revealed through the slit, like the paintings. */
.gl-read-fly{position:fixed;left:0;top:0;overflow:hidden;pointer-events:none;box-sizing:border-box;
  background:linear-gradient(160deg,#2a0010,#1c0009 70%,#15000a);
  border:1px solid rgba(253,233,230,.22);}
.gl-stage .gl-read-fly{z-index:2;}
.gl-read-fly .row{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;}
.gl-read-fly .et{font-family:"IM Fell English",serif;font-style:italic;font-weight:400;
  line-height:1.1;color:var(--blush);white-space:nowrap;}
.gl-read-fly .em{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--blush-dim);white-space:nowrap;}
.gl-read-fly .ex{margin-top:.7rem;font-family:"IM Fell English",serif;font-style:italic;font-size:14.5px;
  color:var(--blush-dim);line-height:1.55;}

/* CRT line-work is OFF by default — the glass starts quiet. `crt on` in the
   terminal (shell.js) lights up the page scanlines, the in-window scanlines
   and the flicker film; `crt off` quiets them again. The vignette always
   stays (it's the monitor's glass edge, not line-work), and the boot
   overlay's own scanlines (.boot::after) keep the BIOS moment. */
.scan,.flick{display:none;}
.window::after{display:none;}
html.crt-on .scan,html.crt-on .flick{display:block;}
html.crt-on .window::after{display:block;}

/* ============================================================
   HOME ROOM — the landing's region content (features · quotes ·
   favourites · ornament band), rendered by gallery-render.js's
   homeHTML. Lives here (not inline on home.html) because an
   in-document room→home hop builds this region inside ANY page.
   ============================================================ */
.eyebrow.sub{margin:.2rem 0 1rem;font-size:11px;letter-spacing:.4em;}

/* features — three sashiko-bordered cards */
.features{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:clamp(1rem,2.2vw,1.6rem);}
.feature{position:relative;border:1px dashed rgba(253,233,230,.28);background:rgba(58,0,17,.30);
  padding:clamp(1.1rem,2vw,1.5rem) clamp(1rem,1.8vw,1.3rem);display:flex;flex-direction:column;gap:.7rem;
  transition:border-color .2s,background .2s,transform .2s;}
.feature:hover{border-color:rgba(253,233,230,.5);background:rgba(58,0,17,.46);transform:translateY(-2px);}
.feat-orn{width:30px;height:30px;background-image:var(--stitch-star);background-size:contain;background-repeat:no-repeat;
  filter:drop-shadow(0 0 6px rgba(253,233,230,.3));}
.feat-t{font-family:"IM Fell English",serif;font-style:italic;font-size:clamp(18px,2vw,23px);color:var(--blush);
  text-shadow:0 0 7px rgba(253,233,230,.22);font-weight:400;}
.feat-p{font-size:13px;line-height:1.7;color:var(--blush-dim);}

/* quotes — what is my art */
.quotes{display:flex;flex-direction:column;gap:clamp(1.1rem,2.4vw,1.8rem);}
.quote{position:relative;padding:.4rem 0 .4rem clamp(1.6rem,3vw,2.4rem);}
.quote::before{content:"\201C";position:absolute;left:0;top:-.15em;
  font-family:"IM Fell English SC",serif;font-size:clamp(40px,6vw,68px);line-height:1;color:var(--blush-faint);
  text-shadow:var(--glow);}
.quote blockquote{font-family:"IM Fell English",serif;font-style:italic;font-size:clamp(17px,1.9vw,22px);
  line-height:1.7;color:var(--blush);text-shadow:0 0 6px rgba(253,233,230,.2);max-width:64ch;}
.quote figcaption{margin-top:.6rem;font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--blush-dim);}

/* favourite art — gallery cards, but a tighter showcase row */
.gallery.fav{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));}

/* sashiko ornament band — a row of stitched stars around a medallion */
.ornaments{display:flex;align-items:center;justify-content:center;gap:clamp(.8rem,2vw,1.6rem);
  margin:clamp(1.4rem,3vw,2.2rem) 0 .4rem;opacity:.9;}
.ornaments .orn-star{width:26px;height:26px;background-image:var(--stitch-star);background-size:contain;
  background-repeat:no-repeat;opacity:.7;}
.ornaments .orn-line{flex:0 1 120px;height:20px;
  background-image:var(--sashiko-diamond),var(--sashiko-thread);
  background-repeat:space no-repeat,no-repeat;
  background-size:48px 20px,100% 1.3px;
  background-position:center,center;opacity:.7;}
.ornaments .orn-med{font-size:22px;color:var(--blush);text-shadow:var(--glow);}

