  :root{
    --case:#cdbfa0;            /* cheap beige plastic */
    --case-dark:#9c8d6e;
    --case-edge:#7d6f54;
    --lcd-bg:#7c8c63;          /* sickly green LCD */
    --lcd-bg2:#94a374;
    --lcd-ink:#1c2410;
    --red:#b6402f;
    --red-dark:#8a2c1e;
    --gold:#c9a227;
    --key:#e9e2d0;
    --key-edge:#b3a988;
    --ink:#2a2418;
    --paper:#f3ead2;
  }
  *{box-sizing:border-box;}
  html,body{margin:0;height:100%;}
  body{
    min-height:100%;
    display:flex;align-items:center;justify-content:center;
    /* respect the notch / home-bar when installed as a standalone PWA */
    padding:
      max(32px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right))
      max(32px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
    background:
      radial-gradient(120% 120% at 20% 10%, #5a5446 0%, #2e2a22 55%, #1b1813 100%);
    font-family:'Special Elite',serif;
    color:var(--ink);
  }
  /* faint desk grain */
  body::before{
    content:"";position:fixed;inset:0;pointer-events:none;opacity:.06;mix-blend-mode:overlay;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  }

  .device{
    position:relative;
    width:340px;max-width:100%;
    background:linear-gradient(160deg,var(--case) 0%,var(--case-dark) 100%);
    border-radius:26px 26px 30px 30px;
    padding:18px 18px 22px;
    box-shadow:
      0 2px 0 #fff6 inset,
      0 -6px 14px #0003 inset,
      0 24px 50px -12px #000a,
      0 4px 0 var(--case-edge);
    border:1px solid #00000022;
    overflow:hidden;            /* clip the tape to the case so it can't float in the background */
  }
  /* aging Sellotape wrapping over the top-right corner of the case
     (the book: "the back had half fallen off… stuck back on with Sellotape") */
  .tape{
    position:absolute; top:-4px; right:6px; z-index:8;
    width:92px; height:26px;
    transform:rotate(40deg);
    /* a shiny, translucent strip lying across the corner; edge highlights along the
       long sides sell it as tape. The case `overflow:hidden` clips it at the rounded
       silhouette, so it reads as going over the corner without floating in the air */
    background:linear-gradient(180deg,
      rgba(255,252,214,.20) 0%,        /* top edge of the tape, catching light */
      rgba(230,212,132,.34) 45%,
      rgba(212,194,116,.30) 55%,
      rgba(255,252,214,.18) 100%);     /* bottom edge of the tape */
    box-shadow:inset 0 0 8px rgba(255,255,255,.30);
    border-left:1px solid rgba(255,255,255,.40);
    border-right:1px solid rgba(150,128,68,.40);
    pointer-events:none;
  }
  .tape::after{   /* glossy diagonal glare — sells the plastic tape */
    content:""; position:absolute; inset:0;
    background:linear-gradient(115deg, rgba(255,255,255,.32) 0 15%, transparent 38%);
  }
  /* brand plate */
  .brand{
    position:relative;
    display:flex;align-items:baseline;justify-content:space-between;gap:8px;
    margin:2px 4px 6px;
  }
  /* "Designed by the Sirius Cybernetics" — absolutely positioned below the wordmark
     so it doesn't push the LED strip down (sticker overlap with LEDs is preserved).
     Small Silkscreen, half-faded by wear. Positioned LOW enough that the £20 sticker
     covers part of it — the manufacturer credit is half-obscured by a second-hand
     price tag, which is exactly how cheap used products feel. Intentional. */
  .brand .designer{
    position:absolute; left:0; top:38px;
    font-family:'Silkscreen',monospace; font-size:7px;
    color:#1f1610; letter-spacing:.5px; line-height:1;
    opacity:.72; white-space:nowrap; pointer-events:none;
  }
  .brand .logo{
    font-family:'Arial Black','Helvetica Neue',Impact,sans-serif;
    font-style:italic;font-weight:900;
    font-size:20px;letter-spacing:-0.3px;color:#1a1a1a;
    text-shadow:0 1px 0 #fff8;
    line-height:1;
  }
  .brand .logo .cj{
    font-family:'Hiragino Mincho ProN','Songti SC','Noto Serif CJK SC',serif;
    font-style:normal;font-weight:900;
    font-size:1.15em;color:#6e1810;
    line-height:1;
  }
  .brand .logo b{color:var(--gold);}
  .brand .model{
    font-family:'Silkscreen',monospace;font-size:8px;color:#1f1610;text-align:right;line-height:1.5;
  }
  /* the "solar strip" is actually a recessed row of nostalgic green LEDs,
     populated and animated by the script (classic blinkenlights patterns) */
  .solar{
    height:15px;margin:0 4px 3px;border-radius:3px;
    background:linear-gradient(180deg,#0d130c,#070a06);
    border:1px solid #00000066;
    box-shadow:0 1px 0 #ffffff22, inset 0 1px 3px #000a;
    display:flex;align-items:center;justify-content:space-between;
    padding:0 5px;overflow:hidden;
  }
  /* Silkscreen LED legend: small faded labels below the LED bar, evidence that the
     factory MEANT the bar to convey status. ERR sits over the SECOND-to-last LED,
     while the actual ERR LED (the soup-twitch) is the THIRD-to-last — off by one.
     The "design was specified with intent, but the offset was wrong" gag. Discovery
     trigger: user produces a soup state, sees the 3rd-from-last LED twitch, looks
     at the ERR label below, realises they don't line up. The whole "failed status
     system" gag is grounded in this one off-by-one. */
  .solar-legend{
    margin:0 4px 8px;
    padding:0 20px 0 5px;        /* extra right padding shifts ERR one LED-width inward */
    font-family:'Silkscreen',monospace; font-size:7px;
    color:#1f1610; opacity:.58;
    letter-spacing:.5px; line-height:1;
    text-align:right; white-space:nowrap;
    pointer-events:none;
  }
  .solar .led{
    --on:0;
    width:8px;height:8px;border-radius:50%;flex:0 0 auto;
    background:radial-gradient(circle at 50% 35%,
      rgba(190,255,150,calc(.18 + .82*var(--on))) 0%,
      rgba(54,170,40,calc(.12 + .60*var(--on))) 55%,
      rgba(8,34,6,.92) 100%);
    box-shadow:0 0 calc(1px + 7px*var(--on)) rgba(120,255,70,calc(.75*var(--on))),
               inset 0 0 2px rgba(0,0,0,.5);
    transition:background .09s linear, box-shadow .09s linear;
  }

  /* ===========================================================
     WEAR & TEAR — this unit has lived a hard, cheap life.
     Grime layers sit at z-index:-1 (behind the keys/LCD/receipt)
     so they dirty the bare case without hurting readability.
     =========================================================== */
  .device{ z-index:0; }   /* stacking context for the grime layers */
  .device::before{        /* grime, blotchy sun-yellowing, greasy edge dirt */
    content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
    border-radius:inherit; mix-blend-mode:multiply;
    background:
      radial-gradient(135% 120% at 50% -12%, transparent 50%, rgba(46,36,18,.46)),
      radial-gradient(120% 120% at -10% 110%, transparent 52%, rgba(38,30,14,.50)),
      radial-gradient(120% 120% at 110% 74%, transparent 54%, rgba(38,30,14,.44)),
      radial-gradient(42% 32% at 80% 20%, rgba(150,118,38,.32), transparent 70%),
      radial-gradient(34% 24% at 18% 60%, rgba(120,90,30,.30), transparent 72%),
      radial-gradient(46% 30% at 64% 92%, rgba(96,72,30,.28), transparent 70%),
      radial-gradient(26% 18% at 36% 34%, rgba(80,58,22,.24), transparent 72%),
      radial-gradient(20% 14% at 92% 46%, rgba(70,50,20,.26), transparent 74%),
      radial-gradient(18% 12% at 8% 32%, rgba(64,48,20,.22), transparent 74%);
  }
  .device::after{         /* fine dust + scuffs & hairline scratches */
    content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
    border-radius:inherit; mix-blend-mode:overlay; opacity:.92;
    background-repeat:no-repeat; background-size:100% 100%;
    background-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='240'%20height='420'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='.9'%20numOctaves='2'/%3E%3C/filter%3E%3Crect%20width='100%25'%20height='100%25'%20filter='url(%23n)'%20opacity='.06'/%3E%3Cg%20fill='none'%20stroke-linecap='round'%3E%3Cpath%20d='M28%2066L132%2044'%20stroke='%23fff'%20stroke-opacity='.5'%20stroke-width='.8'/%3E%3Cpath%20d='M150%20120L210%20150'%20stroke='%23fff'%20stroke-opacity='.4'%20stroke-width='.7'/%3E%3Cpath%20d='M44%20250L150%20232'%20stroke='%23fff'%20stroke-opacity='.38'%20stroke-width='.7'/%3E%3Cpath%20d='M64%20330L196%20318'%20stroke='%23000'%20stroke-opacity='.3'%20stroke-width='.8'/%3E%3Cpath%20d='M18%20170L74%20188'%20stroke='%23000'%20stroke-opacity='.26'%20stroke-width='.7'/%3E%3Cpath%20d='M168%2060L206%2082'%20stroke='%23000'%20stroke-opacity='.24'%20stroke-width='.7'/%3E%3Cpath%20d='M96%20384L208%20372'%20stroke='%23fff'%20stroke-opacity='.36'%20stroke-width='.7'/%3E%3Cpath%20d='M30%20372L78%20360'%20stroke='%23000'%20stroke-opacity='.26'%20stroke-width='.7'/%3E%3Cpath%20d='M120%20200L232%20174'%20stroke='%23fff'%20stroke-opacity='.42'%20stroke-width='.7'/%3E%3Cpath%20d='M52%20120L150%2098'%20stroke='%23000'%20stroke-opacity='.28'%20stroke-width='.7'/%3E%3Cpath%20d='M150%20332L66%20300'%20stroke='%23fff'%20stroke-opacity='.34'%20stroke-width='.7'/%3E%3Cpath%20d='M196%20240L120%20264'%20stroke='%23000'%20stroke-opacity='.24'%20stroke-width='.7'/%3E%3Cpath%20d='M86%20410L196%20398'%20stroke='%23fff'%20stroke-opacity='.3'%20stroke-width='.7'/%3E%3C/g%3E%3C/svg%3E");
  }
  .brand .logo{ opacity:.9; }          /* printing rubbed by years of thumbs */
  .brand .model{ opacity:.88; }
  .lcd::before{           /* hazy, smudged, faintly scratched screen */
    content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
    border-radius:inherit; mix-blend-mode:soft-light;
    background:
      radial-gradient(58% 48% at 70% 36%, rgba(255,255,255,.16), transparent 62%),
      radial-gradient(42% 40% at 22% 76%, rgba(30,40,12,.30), transparent 70%),
      linear-gradient(116deg, transparent 49.6%, rgba(255,255,255,.12) 49.6% 49.85%, transparent 49.85%),
      linear-gradient(74deg, transparent 61.5%, rgba(0,0,0,.10) 61.5% 61.7%, transparent 61.7%);
  }
  .keys button.key{ position:relative; }   /* anchor the greasy-key sheen */
  .key[data-d="5"]::after,
  .key[data-act="equals"]::after,
  .key[data-act="iching"]::after,
  .key[data-d="0"]::after{                  /* most-pressed keys: greasy, worn-shiny */
    content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
    background:radial-gradient(62% 72% at 50% 44%, rgba(255,255,255,.22), rgba(255,255,255,0) 66%);
  }
  .keys button.key::before{                 /* every key wears a little dirt & scuff */
    content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
    mix-blend-mode:multiply;
    background:
      radial-gradient(72% 60% at 28% 26%, rgba(58,48,26,.13), transparent 62%),
      radial-gradient(60% 50% at 80% 82%, rgba(48,40,20,.15), transparent 66%);
  }
  /* labels worn faint by years of pressing (the most-used keys) */
  .key[data-d="0"]{ color:rgba(42,36,24,.40); }
  .key[data-d="5"]{ color:rgba(42,36,24,.50); }
  .key[data-d="8"]{ color:rgba(42,36,24,.58); }
  .key[data-d="2"]{ color:rgba(42,36,24,.62); }
  .key[data-act="equals"]{ color:rgba(58,47,8,.50); }
  .key[data-op="+"]{ color:rgba(58,47,8,.62); }
  /* a couple of keys working loose in their sockets — crooked, sitting proud or sunk */
  .key[data-d="9"]{
    transform:rotate(3deg) translateY(-1px); z-index:1;
    box-shadow:0 4px 0 var(--key-edge),0 7px 8px #0005,0 1px 0 #fff9 inset;
  }
  .key[data-d="1"]{
    transform:rotate(-2.5deg) translateY(1px);
    box-shadow:0 1px 0 var(--key-edge),0 1px 3px #0006,0 1px 0 #fff5 inset;
  }
  .key[data-fn="tan"]{ transform:rotate(1.6deg); }

  /* a peeling thrift-shop price sticker slapped on the case (the device's own
     price is, by its Rule of 4, an unspeakable "Suffusion of Yellow") */
  .sticker{
    position:absolute; left:170px; top:43px; z-index:1; pointer-events:none;
    width:50px; height:34px; transform:rotate(-6deg);
    /* Aged paper: yellowed base with subtle stain blotches from years of grime */
    background:
      radial-gradient(ellipse at 28% 38%, rgba(110,70,30,.18) 0%, transparent 48%),
      radial-gradient(ellipse at 72% 70%, rgba(90,55,20,.14) 0%, transparent 42%),
      radial-gradient(ellipse at 55% 22%, rgba(140,100,50,.10) 0%, transparent 35%),
      linear-gradient(160deg,#dccfa3,#b8a578);
    color:#6d2417; opacity:.82;
    font-family:'Special Elite',serif; font-size:16px; line-height:1; letter-spacing:.5px;
    display:flex; align-items:center; justify-content:center; text-align:center;
    border-radius:2px;
    box-shadow:0 2px 5px rgba(0,0,0,.32), inset 0 0 8px rgba(80,55,20,.10);
  }
  .sticker sup{ font-size:.58em; vertical-align:.55em; }
  .sticker::after{   /* the curled, peeling top-right corner (sticker's pale back, also aged) */
    content:""; position:absolute; top:-3px; right:-4px;
    width:20px; height:18px;
    background:linear-gradient(220deg,#e3d8b0 0%,#ad9f78 82%);
    border-radius:0 2px 0 11px;
    transform:skewX(-10deg) rotate(5deg);
    box-shadow:-2px 3px 4px rgba(0,0,0,.28);
  }

  /* a tired LCD: faint always-on segment ghost, with one segment stuck dark */
  .lcd .stuck{
    position:absolute; left:16px; bottom:11px; z-index:-1; pointer-events:none;
    font-family:'VT323',monospace; font-size:46px; line-height:1;
    color:var(--lcd-ink); opacity:.12;
  }
  .lcd .stuck::after{   /* one segment genuinely stuck ON */
    content:""; position:absolute; left:7px; top:9px; width:19px; height:6px;
    background:var(--lcd-ink); opacity:.45; border-radius:1px;
  }

  /* a hairline crack across the LCD glass (dark fracture + a bright glint) */
  .lcd .crack{
    position:absolute; inset:0; z-index:5; pointer-events:none;
    background-repeat:no-repeat; background-size:100% 100%;
    background-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='280'%20height='110'%3E%3Cg%20fill='none'%20stroke-linejoin='round'%20stroke-linecap='round'%3E%3Cpath%20d='M44%206L92%2040L118%2033L165%2066L150%2072L214%20100'%20stroke='%231c2410'%20stroke-opacity='.32'%20stroke-width='1.6'/%3E%3Cpath%20d='M118%2033L130%2060'%20stroke='%231c2410'%20stroke-opacity='.28'%20stroke-width='1.1'/%3E%3Cpath%20d='M44%206L92%2040L118%2033L165%2066L150%2072L214%20100'%20stroke='%23fff'%20stroke-opacity='.5'%20stroke-width='.6'/%3E%3Cpath%20d='M118%2033L130%2060'%20stroke='%23fff'%20stroke-opacity='.45'%20stroke-width='.5'/%3E%3C/g%3E%3C/svg%3E");
  }

  /* Dead pixels: a few stuck permanently OFF. Rather than DRAW dots (which would
     visibly "light up"), we MASK the readout — punching tiny holes in the text so
     it simply never goes dark at those spots and the real screen shows through.
     Invisible on a blank display; seen only as gaps where dark text passes under. */
  .lcd .out{
    -webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='276'%20height='56'%3E%3Cmask%20id='h'%3E%3Crect%20width='100%25'%20height='100%25'%20fill='%23fff'/%3E%3Ccircle%20cx='28'%20cy='18'%20r='2'/%3E%3Ccircle%20cx='52'%20cy='40'%20r='2.5'/%3E%3Ccircle%20cx='80'%20cy='12'%20r='1.5'/%3E%3Ccircle%20cx='104'%20cy='46'%20r='2'/%3E%3Ccircle%20cx='128'%20cy='24'%20r='2.5'/%3E%3Ccircle%20cx='150'%20cy='36'%20r='2'/%3E%3Ccircle%20cx='172'%20cy='16'%20r='1.5'/%3E%3Ccircle%20cx='196'%20cy='44'%20r='2'/%3E%3Ccircle%20cx='218'%20cy='28'%20r='2.5'/%3E%3Ccircle%20cx='240'%20cy='20'%20r='2'/%3E%3Ccircle%20cx='262'%20cy='42'%20r='2'/%3E%3Ccircle%20cx='40'%20cy='34'%20r='1.5'/%3E%3Ccircle%20cx='116'%20cy='8'%20r='2'/%3E%3Ccircle%20cx='186'%20cy='50'%20r='2'/%3E%3Ccircle%20cx='252'%20cy='12'%20r='1.5'/%3E%3Ccircle%20cx='90'%20cy='30'%20r='2'/%3E%3C/mask%3E%3Crect%20width='100%25'%20height='100%25'%20fill='%23fff'%20mask='url(%23h)'/%3E%3C/svg%3E");
            mask-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='276'%20height='56'%3E%3Cmask%20id='h'%3E%3Crect%20width='100%25'%20height='100%25'%20fill='%23fff'/%3E%3Ccircle%20cx='28'%20cy='18'%20r='2'/%3E%3Ccircle%20cx='52'%20cy='40'%20r='2.5'/%3E%3Ccircle%20cx='80'%20cy='12'%20r='1.5'/%3E%3Ccircle%20cx='104'%20cy='46'%20r='2'/%3E%3Ccircle%20cx='128'%20cy='24'%20r='2.5'/%3E%3Ccircle%20cx='150'%20cy='36'%20r='2'/%3E%3Ccircle%20cx='172'%20cy='16'%20r='1.5'/%3E%3Ccircle%20cx='196'%20cy='44'%20r='2'/%3E%3Ccircle%20cx='218'%20cy='28'%20r='2.5'/%3E%3Ccircle%20cx='240'%20cy='20'%20r='2'/%3E%3Ccircle%20cx='262'%20cy='42'%20r='2'/%3E%3Ccircle%20cx='40'%20cy='34'%20r='1.5'/%3E%3Ccircle%20cx='116'%20cy='8'%20r='2'/%3E%3Ccircle%20cx='186'%20cy='50'%20r='2'/%3E%3Ccircle%20cx='252'%20cy='12'%20r='1.5'/%3E%3Ccircle%20cx='90'%20cy='30'%20r='2'/%3E%3C/mask%3E%3Crect%20width='100%25'%20height='100%25'%20fill='%23fff'%20mask='url(%23h)'/%3E%3C/svg%3E");
    -webkit-mask-size:100% 100%;  mask-size:100% 100%;
    -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  }

  /* LCD */
  .lcd{
    position:relative;z-index:3;          /* sits above the receipt so paper feeds from behind it */
    background:linear-gradient(180deg,var(--lcd-bg2),var(--lcd-bg));
    border-radius:8px;
    margin:0 2px 14px;
    padding:8px 12px 10px;
    box-shadow:0 2px 8px #0004 inset,0 0 0 3px #5d5238,0 0 0 4px #00000022,
               0 8px 10px -6px #0007;     /* slot casts a shadow onto the emerging paper */
    overflow:hidden;
  }
  .lcd::after{ /* glass sheen */
    content:"";position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(115deg,#ffffff33 0%,#ffffff00 40%);
  }
  .lcd .status{
    display:flex;justify-content:space-between;align-items:center;
    font-family:'VT323',monospace;font-size:14px;color:var(--lcd-ink);opacity:.75;
    height:16px;letter-spacing:1px;
  }
  .lcd .status .ann{visibility:hidden;}
  .lcd .status .ann.on{visibility:visible;}
  .lcd .out{
    font-family:'VT323',monospace;color:var(--lcd-ink);
    /* FIXED height: tall enough for the 48px number AND a 2-line wrapped phrase,
       so the LCD never resizes whether the answer is "0", "Beige", or a wrapper */
    text-align:right;line-height:1;height:56px;overflow:hidden;
    display:flex;align-items:center;justify-content:flex-end;
    font-size:48px;word-break:break-word;
  }
  .lcd .out.phrase{font-size:24px;text-align:right;line-height:1.1;letter-spacing:.5px;}
  /* the judgement marching across the tiny LCD */
  .lcd .out.ticker{justify-content:flex-start;overflow:hidden;font-size:30px;padding-left:42px;clip-path:inset(0 0 0 40px);-webkit-clip-path:inset(0 0 0 40px);}
  /* When the hex graphic is visible, the SETTLED phrase ("易 No.N NAME") also
     needs to keep clear of the hex on the left — otherwise long names overflow
     leftward (right-aligned text wrapping) and run under the hexagram. */
  .lcd.has-hex .out.phrase{
    padding-left:42px;
    clip-path:inset(0 0 0 40px);
    -webkit-clip-path:inset(0 0 0 40px);
  }
  .lcd .out.ticker .run{
    white-space:nowrap;display:inline-block;padding-left:100%;
    animation:march 5s linear 1;
  }
  @keyframes march{from{transform:translateX(0);}to{transform:translateX(-100%);}}

  /* ── The hexagram graphic: appears at the left of the LCD during a consult.
     Lines render bottom-up (the hex-lines flex is column-reverse, so the first
     child appears at the bottom). For "deluxe" (numbers 65–128), the LCD glitches
     after line 6 and a faint, crooked, blinking 7th line is added on top. ── */
  .lcd .hex{
    position:absolute; left:12px; top:26px; z-index:4;
    width:26px; opacity:0; transition:opacity .25s ease;
    pointer-events:none;
  }
  .lcd .hex.on{opacity:1;}
  .lcd .hex-lines{
    display:flex; flex-direction:column-reverse;
    gap:2px; height:33px;  /* reserves a 7-line slot in both modes, so the num doesn't jump */
  }
  .lcd .hex-line{
    height:3px; display:flex; gap:3px;
    opacity:0; transition:opacity 140ms ease-out;
  }
  .lcd .hex-line.show{opacity:1;}
  .lcd .hex-line.yang::before{
    content:""; flex:1; background:var(--lcd-ink); border-radius:1px;
  }
  .lcd .hex-line.yin::before, .lcd .hex-line.yin::after{
    content:""; flex:0 0 46%; background:var(--lcd-ink); border-radius:1px;
  }
  /* the bonus 7th line: crooked + faint + irregular blink, like a finger pressed
     into the LCD where this line shouldn't exist */
  .lcd .hex-line.deluxe{
    transform:rotate(-3.5deg) translateX(1px);
    transform-origin:left center;
    transition:none;
  }
  .lcd .hex-line.deluxe.show{
    opacity:.5;
    animation:lcdStruggle 4.3s infinite ease-in-out;
  }
  @keyframes lcdStruggle{
    0%   { opacity:.50; filter:blur(0); }
    14%  { opacity:.62; }
    27%  { opacity:.08; filter:blur(.4px); }
    34%  { opacity:.44; filter:blur(.2px); }
    48%  { opacity:.55; filter:blur(0); }
    61%  { opacity:.70; }
    74%  { opacity:.50; }
    88%  { opacity:.38; filter:blur(.15px); }
    100% { opacity:.50; }
  }
  .lcd .hex-num{
    margin-top:3px; text-align:center; line-height:1;
    font-family:'VT323',monospace; font-size:9px;
    color:var(--lcd-ink); opacity:.72; letter-spacing:.5px;
  }

  /* The pie symbol — occupies the same left-most LCD area as the hexagram graphic,
     but only fires for the π+π = PIE reading. A narrow wedge cut from the upper-
     right area (NOT a 90° gap, which would read as a Pac-Man mouth): the slice
     was removed cleanly, served and eaten elsewhere. The angle is deliberately
     off-cardinal so it doesn't align with any iconic game-character mouth shape.
     Sits below the DEG annunciator so it doesn't crowd the status bar. Same
     fade-in transition as .hex; hidden by any non-pie action via hidePie(). */
  .lcd .pie{
    position:absolute; left:9px; top:36px; z-index:4;
    width:30px; height:30px; border-radius:50%;
    background:conic-gradient(from 20deg, transparent 0 50deg, var(--lcd-ink) 50deg 360deg);
    opacity:0; transition:opacity .25s ease;
    pointer-events:none;
  }
  .lcd .pie.on{ opacity:.72; }

  /* the breakdown overlay: scanline-y + jittery, one short panic and gone */
  .lcd .lcd-glitch{
    position:absolute; inset:0; z-index:6; pointer-events:none;
    background:repeating-linear-gradient(0deg, var(--lcd-ink) 0 1px, transparent 1px 4px);
    mix-blend-mode:multiply; opacity:0;
  }
  .lcd.glitch .lcd-glitch{
    animation:lcdPanic 1.2s linear 1;
  }
  @keyframes lcdPanic{
    0%   { opacity:0;   transform:translateY(0); }
    8%   { opacity:.85; transform:translateY(2px); }
    18%  { opacity:.50; transform:translateY(-3px); }
    30%  { opacity:.80; transform:translateY(1px); }
    42%  { opacity:.30; transform:translateY(-2px); }
    55%  { opacity:.70; transform:translateY(0); }
    70%  { opacity:.40; transform:translateY(-1px); }
    85%  { opacity:.80; transform:translateY(1px); }
    100% { opacity:0;   transform:translateY(0); }
  }

  /* BATTERY LOW — the device's pretend-energy-source giving out. Only the
     SEGMENTS fade (text, annunciators, ghost-8, hex/pie graphics) and the
     individual LEDs. The green LCD panel itself stays solid throughout (the
     device emulates a cheap reflective LCD — no backlight; the green is glass
     and reflector, not powered). Same for the LED bar housing — the dark
     plastic stays; only the LEDs lose current. ease-in gives the battery-knee
     feel: slow plateau, accelerating drop. "forwards" keeps the final state
     (opacity:0) until the class is removed.

     Two separate classes so the LEDs can come back online 2 seconds before
     the LCD does (wake-up sequence: LEDs sputter randomly as "power returns,"
     then the LCD finally shows "0"):
       .dying-lcd  — fades the LCD segments. Removed at full wake-up.
       .dying-leds — fades the LED bar. Removed 2s before wake-up so the
                     wake-LED show is visible. */
  @keyframes batteryDie {
    from { opacity: 1; }
    to   { opacity: 0; }
  }
  .dying-lcd .lcd .status,
  .dying-lcd .lcd .out,
  .dying-lcd .lcd .stuck,
  .dying-lcd .lcd .hex,
  .dying-lcd .lcd .pie,
  .dying-leds .solar .led {
    animation: batteryDie var(--battery-die-duration, 10s) ease-in forwards;
  }

  /* (No printout: the whole reading scrolls across the LCD instead — see the
     marquee styles above and consult() in the script.) */

  /* keypad */
  .keys{display:grid;grid-template-columns:repeat(4,1fr);gap:9px;margin:0 2px;}
  button.key{
    appearance:none;border:none;cursor:pointer;
    font-family:'Silkscreen',monospace;font-size:15px;
    padding:14px 0;border-radius:9px;
    background:linear-gradient(180deg,#fff8 0%,var(--key) 30%,var(--key) 70%,var(--key-edge) 100%);
    color:var(--ink);
    box-shadow:0 2px 0 var(--key-edge),0 3px 4px #0004,0 1px 0 #fff9 inset;
    transition:transform .04s,box-shadow .04s;
    user-select:none;
  }
  button.key:active{transform:translateY(2px);box-shadow:0 0 0 var(--key-edge),0 1px 2px #0006;}
  button.op{
    background:linear-gradient(180deg,#ffb,var(--gold));color:#3a2f08;
    box-shadow:0 2px 0 #8a6f15,0 3px 4px #0004,0 1px 0 #fff7 inset;
  }
  button.op:active{box-shadow:0 0 0 #8a6f15,0 1px 2px #0006;}
  button.sci{
    background:linear-gradient(180deg,#dfe6ea,#a9b8c0);color:#1d2a30;font-size:12px;padding:11px 0;
    box-shadow:0 2px 0 #7e909a,0 3px 4px #0004,0 1px 0 #fff8 inset;
  }
  button.sci:active{box-shadow:0 0 0 #7e909a,0 1px 2px #0006;}
  button.red{
    background:linear-gradient(180deg,#e9806f,var(--red));color:#fff;
    box-shadow:0 2px 0 var(--red-dark),0 3px 4px #0004,0 1px 0 #fff4 inset;
  }
  button.red:active{box-shadow:0 0 0 var(--red-dark),0 1px 2px #0006;}
  button.eq{grid-row:span 2;display:flex;align-items:center;justify-content:center;}
  button.zero{grid-column:span 2;}
  /* The "red button" the manual tells you to press. There isn't one —
     there is a blue button marked "Red". (Dirk Gently, faithfully.)
     Sits in the bottom row, same combined width as 0 and . — narrower than
     the original full-width oracle bar, so the layout reads less neat. */
  button.iching{
    grid-column:span 3;margin-top:2px;font-size:16px;
    background:linear-gradient(180deg,#5b8fc2 0%,#3f72a8 45%,#2f5985 100%);color:#f3f6fb;
    box-shadow:0 2px 0 #1f3f63,0 3px 6px #0006,0 1px 0 #ffffff33 inset;
    letter-spacing:1px;
  }
  button.iching:active{box-shadow:0 0 0 #1f3f63,0 1px 2px #0007;}
  button.iching .hz{font-family:'VT323',monospace;font-size:20px;margin-right:8px;vertical-align:-2px;color:var(--gold);}
  button.iching .mark{font-family:'Silkscreen',monospace;}

  .manual-link{
    display:block;width:100%;margin-top:12px;text-align:center;
    font-family:'Silkscreen',monospace;font-size:9px;color:#b6a677;
    text-shadow:0 1px 0 rgba(0,0,0,.35);
    background:none;border:none;cursor:pointer;letter-spacing:.5px;
  }
  .manual-link:hover{color:#e7d39a;text-decoration:underline;}

  /* manual modal */
  .modal{
    position:fixed;inset:0;background:#000a;display:none;
    align-items:center;justify-content:center;padding:24px;z-index:9;
  }
  .modal.open{display:flex;}
  .booklet{
    background:var(--paper);max-width:380px;width:100%;max-height:80vh;overflow:auto;
    padding:22px 24px;border-radius:3px;box-shadow:0 20px 60px #000b;
    font-family:'Special Elite',serif;color:#241d10;font-size:13px;line-height:1.5;
    transform:rotate(-.4deg);
  }
  .booklet h2{font-family:'Silkscreen',monospace;font-size:13px;color:var(--red-dark);
    margin:0 0 4px;letter-spacing:.5px;}
  .booklet .sub{font-family:'Silkscreen',monospace;font-size:8px;color:#8a7c55;margin-bottom:14px;}
  .booklet p{margin:0 0 10px;}
  .booklet .missing{
    border:2px dashed #b6402f88;color:#8a2c1e;padding:10px;text-align:center;
    font-family:'Silkscreen',monospace;font-size:9px;line-height:1.8;margin:12px 0;
  }
  .booklet .warranty{font-size:11px;color:#5a4f38;border-top:1px solid #00000022;padding-top:10px;margin-top:6px;}
  .booklet .close{
    margin-top:14px;width:100%;padding:9px;cursor:pointer;border:none;border-radius:4px;
    background:var(--red);color:#fff;font-family:'Silkscreen',monospace;font-size:10px;
  }

  /* DEG annunciator — dimmed (moved off an inline style for CSP) */
  .lcd .status .ann.deg{opacity:.6;}

  /* PWA install banner */
  .install-banner{
    position:fixed;bottom:0;left:0;right:0;
    display:flex;align-items:center;justify-content:center;gap:12px;
    padding:10px 16px;
    background:#2a2418;border-top:2px solid var(--gold);
    font-family:'Silkscreen',monospace;font-size:10px;color:var(--paper);
    transform:translateY(100%);transition:transform .3s ease;
    z-index:200;
    padding-bottom:max(10px, env(safe-area-inset-bottom));
  }
  .install-banner.visible{transform:translateY(0);}
  .install-text{letter-spacing:.5px;}
  .install-btn{
    padding:5px 14px;border:none;border-radius:4px;cursor:pointer;
    background:var(--red);color:#fff;
    font-family:'Silkscreen',monospace;font-size:10px;letter-spacing:.5px;
  }
  .install-btn:hover{background:var(--red-dark);}
  .install-dismiss{
    background:none;border:none;color:var(--case-dark);cursor:pointer;
    font-size:14px;padding:4px 6px;line-height:1;
  }
  .install-dismiss:hover{color:var(--paper);}
