/* ===== Stage & zones ==================================================== */
:root{
  --board-font: 'Fira Code', monospace;
  --glyph-weight: 600;
}
html, body{ margin: 0; padding: 0; height: 100%; }
body{
  background: var(--bg);
  overflow: hidden;
  font-family: var(--board-font);
}
#stage{
  height: 100svh;
  display: flex;
  flex-direction: column;
  position: relative;
}
/* photographic finish: sensor grain + gentle corner falloff. Flat CSS renders
   are perfectly even and noise-free, which is the fastest tell that they
   aren't a photo of a physical board — a hair of both fixes that cheaply. */
#stage::before{
  content: '';
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: .05;
  mix-blend-mode: overlay;
}
#stage::after{
  content: '';
  position: absolute;
  inset: 0;
  z-index: 11;
  pointer-events: none;
  background: radial-gradient(120% 90% at 50% 42%, transparent 55%, rgba(0,0,0,.35) 100%);
}
.zone{
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.board-grid{
  display: grid;
  grid-template-columns: repeat(var(--cols, 1), max-content);
  gap: var(--flap-gap);
  padding: var(--flap-gap);
  position: relative;
}
/* modules are bolted to a backplate — a hint of rivets in the seams sells
   "individually mounted components" rather than one continuous CSS grid */
.board-grid::before{
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--rivet-opacity, 0);
  background-image: radial-gradient(circle, rgba(225,225,230,1) 0 1.3px, rgba(225,225,230,.35) 1.3px 2px, transparent 2.3px);
  background-size: calc(var(--flap-w) + var(--frame) + var(--flap-gap)) calc(var(--flap-h) + var(--frame) + var(--flap-gap));
  background-position: calc(var(--flap-gap) * .5) calc(var(--flap-gap) * .5);
}

/* ===== Zone chrome: painted (non-flap) board areas ====================== */
.zone-box{ display: flex; flex-direction: column; align-items: flex-start; }
.zone-row{ display: flex; align-items: center; }
.zone-title{
  padding: 0 calc(var(--flap-gap) * 2);
  font-size: calc(var(--flap-h) * .34);
  font-weight: var(--glyph-weight);
  letter-spacing: .18em;
  color: var(--txt);
  opacity: .92;
}
.board-captions{
  display: grid;
  grid-template-columns: repeat(var(--cols, 1), calc(var(--flap-w) + var(--frame)));
  column-gap: var(--flap-gap);
  padding: 0 var(--flap-gap);
  width: 100%;
  box-sizing: border-box;
}
.board-captions span{
  font-size: calc(var(--flap-h) * .17);
  letter-spacing: .14em;
  color: var(--txt);
  opacity: .55;
  white-space: nowrap;
  overflow: visible;
}
.zone-footer{
  align-self: center;
  padding: calc(var(--flap-gap) / 2);
  font-size: calc(var(--flap-h) * .16);
  letter-spacing: .18em;
  color: var(--txt);
  opacity: .45;
}
.zone-lead{
  font-size: calc(var(--flap-h) * .30);
  font-weight: var(--glyph-weight);
  letter-spacing: .12em;
  color: var(--txt);
  opacity: .75;
  padding-left: var(--flap-gap);
  white-space: nowrap;
}

/* static cells: glyphs painted on the board itself (e.g. the clock ':') */
.tile--static{
  width:  calc(var(--flap-w) + var(--frame));
  height: calc(var(--flap-h) + var(--frame));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--flap-h) * var(--glyph-size));
  font-weight: var(--glyph-weight);
  color: var(--txt);
  opacity: .9;
}

/* ===== Theme variables (defaults = Classic / Solari) =====================
   Tonal hierarchy (all neutral steel-gray, never warm/brown): the cutout
   frame (--well) is the DARKEST tone — it's a thin recessed edge, the plate
   mounted in front of the mechanism. The flap faces (--face-*) are the
   LIGHTEST, glossiest tone — they're the part that physically tilts toward
   the viewer and catches the most light. --tile-bg (housing) sits between. */
body{
  --bg: #0a0a0b;
  --tile-bg1: #232326; --tile-bg2: #131315;
  --well1: #08080a;    --well2: #020203;
  --face-top1: #2c2c2f; --face-top2: #1a1a1d;
  --face-bot1: #26262a; --face-bot2: #151517;
  --txt: #f0f0f0;
  --hinge-bg: #020203;
  /* housing/paddle corners are asymmetric (rounder top, squarer foot). These
     are bare ratios (numbers), not calc()-valued custom properties — Chrome
     silently fails to repaint border-radius when it's set from a custom
     property whose OWN value is a calc() referencing another custom
     property. The multiply-by-flap-w happens once, inline, at the point of
     use (see .tile / .tile__well / .tile__flap below). */
  --tile-r-ratio: .045;     --tile-r-top-ratio: .09;
  --well-r-ratio: .05;      --well-r-top-ratio: .18; --well-r-bot-ratio: .035;
  --tile-shadow: 0 calc(var(--flap-h) * .05) calc(var(--flap-h) * .10) rgba(0,0,0,.45),
                 inset 0 1px 0 rgba(255,255,255,.05);
  --well-shadow: inset 0 2px 6px rgba(0,0,0,.7), inset 0 -1px 0 rgba(255,255,255,.02);
  --glyph-size: .78;
  --rivet-opacity: .7;
  --blankface-opacity: 1;
}

body.theme-workshop{
  --bg: #121212;
  --tile-bg1: #232326; --tile-bg2: #131315;
  --tile-r-ratio: .16;      --tile-r-top-ratio: .32;
  --well-r-ratio: .11;      --well-r-top-ratio: .40; --well-r-bot-ratio: .077;
  --tile-shadow: 0 18px 28px rgba(0,0,0,.6), 0 3px 8px rgba(0,0,0,.5),
                 inset 0 1px 0 rgba(255,255,255,.06);
}

/* Vestaboard: sealed consumer unit — uniform matte black, no exposed
   mechanism. Flat, clean, almost no gloss and no visible paddle/pivot. */
body.theme-board{
  --bg: #131314;
  --tile-bg1: #050505; --tile-bg2: #020202;
  --well1: #000;       --well2: #000;
  --face-top1: #0a0a0a; --face-top2: #030303;
  --face-bot1: #080808; --face-bot2: #020202;
  --txt: #fff;
  --hinge-bg: #000;
  --tile-r-ratio: .10;      --tile-r-top-ratio: .20;
  --well-r-ratio: .08;      --well-r-top-ratio: .29; --well-r-bot-ratio: .056;
  --tile-shadow: none;
  --well-shadow: none;
  --rivet-opacity: 0;
  --blankface-opacity: .3;
}

body.theme-terminal{
  --bg: #000;
  --tile-bg1: #000; --tile-bg2: #000;
  --well1: #041008; --well2: #010502;
  --face-top1: #05140b; --face-top2: #030d07;
  --face-bot1: #05140b; --face-bot2: #030d07;
  --txt: #4dff88;
  --hinge-bg: #000;
  --tile-shadow: none; --well-shadow: none;
  --rivet-opacity: 0;
  --blankface-opacity: 0;
}
body.theme-terminal .half{ text-shadow: 0 0 8px rgba(77,255,136,.45); }
/* LED bezel: a small constant radius, not scaled from flap size */
body.theme-terminal .tile,
body.theme-terminal .tile__well,
body.theme-terminal .tile__flap{ border-radius: 2px; }

/* ===== Tile / mechanism ================================================= */
.tile{
  width:  calc(var(--flap-w) + var(--frame));
  height: calc(var(--flap-h) + var(--frame));
  /* the housing is a small "tombstone": rounded where the mechanism cap
     sits, squarer at the foot — plain rectangles read as flat UI chips */
  border-radius: calc(var(--flap-w) * var(--tile-r-top-ratio)) calc(var(--flap-w) * var(--tile-r-top-ratio))
                  calc(var(--flap-w) * var(--tile-r-ratio)) calc(var(--flap-w) * var(--tile-r-ratio));
  background: linear-gradient(var(--tile-bg1), var(--tile-bg2));
  box-shadow: var(--tile-shadow);
  position: relative;
  overflow: hidden;
  filter: brightness(1);
}
/* every physical flap ages/catches light a little differently — a perfectly
   uniform grid is the single biggest "this is obviously CSS" tell */
.tile:nth-child(7n+1){ filter: brightness(1.035); }
.tile:nth-child(11n+3){ filter: brightness(.965); }
.tile:nth-child(13n+5){ filter: brightness(1.02); }
.tile:nth-child(17n+2){ filter: brightness(.98); }
/* glossy plastic cap: a soft diagonal sheen sitting above everything else,
   the way injection-moulded housings catch a highlight across the top */
.tile::before{
  content: '';
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(150deg,
      rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 20%,
      rgba(255,255,255,0) 75%, rgba(255,255,255,.05) 100%);
}
body.theme-board .tile::before{ background: linear-gradient(150deg, rgba(255,255,255,.04) 0%, rgba(255,255,255,0) 16%); }
body.theme-terminal .tile::before{ display: none; }

/* the well is the cutout in the front plate — the darkest, thinnest ring
   visible around the flap face, so the face reads as mounted behind it */
.tile__well{
  position: absolute;
  inset: calc(var(--frame) * .5);
  border-radius: calc(var(--flap-w) * var(--well-r-ratio));
  background: var(--well1);
  box-shadow: var(--well-shadow);
  overflow: hidden;
  perspective: calc(var(--flap-h) * 6);   /* close enough for depth, far enough to avoid streaking */
  perspective-origin: 50% 40%;
}

/* the stack of flaps waiting at the top / already fallen at the bottom */
.tile__well::before, .tile__well::after{
  content: '';
  position: absolute;
  left: 4%; right: 4%;
  height: calc(var(--flap-h) * .05);
  z-index: 5;
  pointer-events: none;
  opacity: var(--stack-opacity, 1);
}
.tile__well::before{
  top: 0;
  border-radius: 0 0 40% 40%;
  background:
    repeating-linear-gradient(180deg,
      rgba(0,0,0,.9) 0, rgba(0,0,0,.9) 1.5px,
      rgba(255,255,255,.07) 1.5px, rgba(255,255,255,.07) 2px,
      rgba(20,20,20,.9) 2px, rgba(20,20,20,.9) 3.5px);
  box-shadow: 0 1px 2px rgba(0,0,0,.7);
}
.tile__well::after{
  bottom: 0;
  border-radius: 40% 40% 0 0;
  background:
    repeating-linear-gradient(0deg,
      rgba(0,0,0,.9) 0, rgba(0,0,0,.9) 1.5px,
      rgba(255,255,255,.05) 1.5px, rgba(255,255,255,.05) 2px,
      rgba(18,18,18,.9) 2px, rgba(18,18,18,.9) 3.5px);
  box-shadow: 0 -1px 2px rgba(0,0,0,.7);
}
body.theme-terminal{ --stack-opacity: 0; }
body.theme-board{ --stack-opacity: .5; }

/* idle/space cells: no character engaged, so the flap rests showing its
   blank back — a single tilted card proud of the frame, not two halves.
   It's the lightest, glossiest surface on the tile (see palette comment
   above) with a visible pivot screw, same as the OAT Foundry / 30th St
   reference photos. */
.tile__flap{
  position: absolute;
  left: 6%; right: 6%; top: 8%; bottom: 3%;
  border-radius: calc(var(--flap-w) * var(--well-r-top-ratio)) calc(var(--flap-w) * var(--well-r-top-ratio))
                  calc(var(--flap-w) * var(--well-r-bot-ratio)) calc(var(--flap-w) * var(--well-r-bot-ratio));
  background:
    linear-gradient(135deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,0) 28%),
    linear-gradient(175deg, var(--face-top1) 0%, var(--face-bot1) 55%, var(--face-bot2) 100%);
  box-shadow: 0 calc(var(--flap-h) * .035) calc(var(--flap-h) * .05) rgba(0,0,0,.6),
              inset 0 1px 0 rgba(255,255,255,.10);
  opacity: 0;
  z-index: 6;
  pointer-events: none;
}
.tile__flap::after{   /* pivot screw */
  content: '';
  position: absolute;
  left: 50%; top: 40%;
  width: calc(var(--flap-w) * .05); height: calc(var(--flap-w) * .05);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle at 35% 32%, rgba(255,255,255,.65), rgba(0,0,0,.55) 75%);
  box-shadow: 0 1px 1px rgba(0,0,0,.6);
}
.tile--blank .tile__flap{ opacity: var(--blankface-opacity, 1); }
body.theme-board .tile__flap::after{ display: none; }   /* sealed unit: no exposed mechanism */

.hinge{
  position: absolute;
  left: 6%; right: 6%;
  top: 50%;
  height: max(2px, calc(var(--flap-h) * .02));
  transform: translateY(-50%);
  background: var(--hinge-bg);
  z-index: 4;
  pointer-events: none;
}
.splitflap{
  position: absolute;
  inset: 8% 6% 3%;
  transform-style: preserve-3d;
}

/* halves are 50% clip windows over a full-height glyph */
.half{
  position: absolute;
  left: 0;
  width: 100%;
  height: 50%;
  overflow: hidden;
  backface-visibility: hidden;
  will-change: transform, opacity;
  font-size: calc(var(--flap-h) * var(--glyph-size));
  font-weight: var(--glyph-weight);
  letter-spacing: .02em;
  color: var(--txt);
}
.half > span{
  display: block;
  width: 100%;
  height: var(--flap-h);
  line-height: var(--flap-h);
  text-align: center;
}
.nextTop, .oldTop{
  top: 0;
  border-top-left-radius: calc(var(--flap-w) * var(--well-r-ratio));
  border-top-right-radius: calc(var(--flap-w) * var(--well-r-ratio));
  transform-origin: bottom;   /* hinge */
  background-image: linear-gradient(0deg, var(--face-top1) 0%, var(--face-top2) 100%);
}
.nextBottom, .oldBottom{
  bottom: 0;
  border-bottom-left-radius: calc(var(--flap-w) * var(--well-r-ratio));
  border-bottom-right-radius: calc(var(--flap-w) * var(--well-r-ratio));
  transform-origin: top;      /* hinge */
  background-image: linear-gradient(180deg, var(--face-bot1) 0%, var(--face-bot2) 100%);
}
.nextBottom > span, .oldBottom > span{
  margin-top: calc(var(--flap-h) * -0.5);
}

/* resting bias avoids z-fighting */
.nextTop   { transform: rotateX(-2deg); z-index: 1; }
.oldTop    { transform: rotateX(-2deg); z-index: 3; }
.nextBottom{ transform: rotateX( 2deg); z-index: 2; }
.oldBottom { transform: rotateX( 3deg); z-index: 0; }

/* ===== Animations ========================================================
   One physical card: front face = oldTop (falls 0→-90°, accelerating under
   gravity), back face = nextBottom (continues -90→0 fast, then bounces).
   Lighting: the card darkens as it tilts away, glints as it slaps down.
   The moving edge gets a highlight (box-shadow) so the card reads as having
   thickness. Keyframes are dense + linear so the curve lives in the values. */
.flipTop{
  animation: flipTop .5s linear forwards;
  box-shadow: 0 1px 0 rgba(255,255,255,.22);   /* card edge */
}
.nextBottom.flipBottom{
  animation: flipBottom .5s linear forwards;
  box-shadow: 0 2px 3px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.16);
}
.oldBottom.flipBottom  { animation: fadeout .5s linear forwards; }

/* the old bottom face must vanish the moment the new card slaps past flat —
   with preserve-3d the overshooting card sorts BEHIND it geometrically */
@keyframes fadeout{ 0%,52%{opacity:1} 58%,100%{opacity:0} }

@keyframes flipTop{      /* gravity: slow release, fast at the hinge */
  0%   { transform: rotateX(-2deg);  filter: brightness(1);   opacity: 1; }
  18%  { transform: rotateX(-9deg);  filter: brightness(.96); }
  32%  { transform: rotateX(-24deg); filter: brightness(.88); }
  42%  { transform: rotateX(-52deg); filter: brightness(.72); }
  49%  { transform: rotateX(-90deg); filter: brightness(.5);  opacity: 1; }
  50%, 100% { transform: rotateX(-90deg); opacity: 0; }
}
@keyframes flipBottom{   /* keeps falling fast, slaps down, tiny rebound */
  0%, 49% { transform: rotateX(90deg);  filter: brightness(.5); opacity: 0; }
  50%  { transform: rotateX(90deg);  filter: brightness(.55); opacity: 1; }
  58%  { transform: rotateX(38deg);  filter: brightness(.8); }
  66%  { transform: rotateX(-6deg);  filter: brightness(1.18); }  /* overshoot + glint */
  76%  { transform: rotateX(6deg);   filter: brightness(1.04); }
  88%  { transform: rotateX(0deg);   filter: brightness(1); }
  100% { transform: rotateX(2deg);   filter: brightness(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce){
  .flipTop, .flipBottom{ animation-duration: .001s !important; }
}
