/* ==========================================================================
   PAUL OPEY SITE: STYLES
   The idea: a dark page with a blueprint grid, sitting inside a thick,
   bolted aluminium frame. Pages change with forge shutters, a scan sweep runs
   inside each page, and the "dazzle" effects are saved for the hidden commands.

   Sections, in order:
     1. Design tokens (colours, fonts, sizes)
     2. Basics (resets and defaults)
     3. Frame and page
     4. Navigation
     5. Typography
     6. "Here as" menu (the door)
     7. Flip cards
     8. Friend easter egg and the card sphere
     9. Layer picker
    10. Peek strip
    11. Values page
    12. Projects page
    13. PaulShell (the fake PowerShell)
    14. Page transitions and toast
    15. Accessibility and small screens
    16. Game page (OPEY.EXE)
   ========================================================================== */


/* ==========================================================================
   1. DESIGN TOKENS
   Every colour, font and size is defined once here, as a CSS variable.
   Change a value here and it changes everywhere it is used on the site.
   ========================================================================== */
:root {
  --stage: #07060b;   /* The darkest colour, behind the whole page. */
  --bg: #0e0b14;      /* The page background, inside the frame. */
  --panel: #171122;   /* Cards and panels, slightly lighter than the page. */
  --line: #35294d;    /* Thin borders and divider lines. */
  --ink: #ece8f4;     /* Main text colour. */
  --mute: #a9a1bc;    /* Quieter text, for less important information. */
  --pur: #6a2fd0;     /* The royal purple used for accents. */
  --pur-hi: #b199ff;  /* A lighter purple, for highlights and active items. */
  --grid: rgba(139,108,255,.13);  /* The faint blueprint grid lines. */
  --glow: transparent;            /* The glow around the frame. Off by default, Overclock mode turns it on. */
  --alu: linear-gradient(135deg, #e9ecf0 0%, #aab0b9 26%, #d9dde2 50%, #8b919b 76%, #c8ccd2 100%);  /* Aluminium: a diagonal light-to-dark metal shine. */
  --brush: repeating-linear-gradient(90deg, rgba(255,255,255,.08) 0 1px, rgba(0,0,0,.06) 1px 2px);  /* Fine vertical lines that make the aluminium look brushed. Laid on top of --alu. */
  --display: 'Big Shoulders Display', 'Arial Narrow', sans-serif;  /* Headings. The fallbacks are used if the first font is not available. */
  --body: 'IBM Plex Sans', system-ui, sans-serif;                  /* Normal text. */
  --mono: 'IBM Plex Mono', ui-monospace, monospace;                /* Terminal-style text: labels, shell, game. */
  --frame: 18px;      /* Thickness of the aluminium frame. Also the size of each bolt. */
  --rad: 8px;         /* The standard corner rounding. */
  color-scheme: dark; /* Tells the browser to draw its own parts (scrollbars, form controls) dark too. */
}

/* Overclock mode: switched on by a hidden command. It only changes a few of the
   tokens above, so the whole site brightens and glows without any other code. */
body[data-mode="overclock"] {
  --grid: rgba(177,153,255,.32);
  --pur-hi: #d6bfff;
  --glow: rgba(150,90,255,.55);
  --line: #5a3f8a;
}


/* ==========================================================================
   2. BASICS
   Small resets so every browser starts from the same place.
   ========================================================================== */
* { box-sizing: border-box; }  /* Padding and borders count inside an element's width, which makes sizes easier to predict. */
html, body { height: 100%; }
body { margin: 0; background: var(--stage); color: var(--ink); font-family: var(--body); line-height: 1.55; }
button { font: inherit; color: inherit; cursor: pointer; }  /* Buttons normally ignore the page font and colour. This makes them follow it. */
a { color: inherit; }
:focus-visible { outline: 2px solid var(--pur-hi); outline-offset: 3px; }  /* The ring shown when someone tabs through the page with the keyboard. */
[hidden] { display: none !important; }  /* Anything marked "hidden" stays hidden, even if another rule tries to show it. */


/* ==========================================================================
   3. FRAME AND PAGE
   .wrap is the space around everything. .frame is the aluminium border.
   .site is the dark blueprint page inside it.
   ========================================================================== */
.wrap { padding: 24px 14px 64px; }
.frame { position: relative; max-width: 1060px; margin: 0 auto; padding: var(--frame); border-radius: 14px; background: var(--brush), var(--alu); box-shadow: 0 30px 80px rgba(0,0,0,.65), 0 0 60px var(--glow), inset 0 1px 0 rgba(255,255,255,.7); transition: box-shadow .6s; }

/* The four bolts, one in each corner of the frame.
   This is a ::before layer that covers the whole frame (but cannot be clicked).
   It paints the same bolt four times, once per corner. Each bolt is a dark
   centre with a light ring around it. Only the corner position differs. */
.frame::before { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background:
    radial-gradient(circle closest-side, #3b3f46 0 30%, #d4d7dc 34% 62%, transparent 66%) top left / var(--frame) var(--frame) no-repeat,
    radial-gradient(circle closest-side, #3b3f46 0 30%, #d4d7dc 34% 62%, transparent 66%) top right / var(--frame) var(--frame) no-repeat,
    radial-gradient(circle closest-side, #3b3f46 0 30%, #d4d7dc 34% 62%, transparent 66%) bottom left / var(--frame) var(--frame) no-repeat,
    radial-gradient(circle closest-side, #3b3f46 0 30%, #d4d7dc 34% 62%, transparent 66%) bottom right / var(--frame) var(--frame) no-repeat; }

/* The dark page inside the frame. It stacks three backgrounds:
   a soft purple glow in the top right, then the horizontal and vertical lines
   of the blueprint grid. The grid squares are 32px, set in background-size. */
.site { position: relative; overflow: hidden; border-radius: var(--rad); background-color: var(--bg);
  background-image: radial-gradient(ellipse at 85% -10%, rgba(106,47,208,.22), transparent 55%), linear-gradient(var(--grid) 1px, transparent 1px), linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: auto, 32px 32px, 32px 32px; padding: 0 clamp(18px, 4vw, 40px) 36px; box-shadow: inset 0 0 0 1px var(--line); min-height: 640px; }


/* ==========================================================================
   4. NAVIGATION
   The top bar with the Paul / Opey mark and the page links.
   Links are at least 44px tall, the usual minimum size for a finger to tap.
   ========================================================================== */
.nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 24px; padding: 14px 0; border-bottom: 1px solid var(--line); }
.mark { font-family: var(--display); font-weight: 800; font-size: 1.6rem; letter-spacing: .06em; text-transform: uppercase; text-decoration: none; min-height: 44px; display: inline-flex; align-items: center; }
.mark span { color: var(--pur-hi); }
.nav ul { display: flex; flex-wrap: wrap; gap: 2px 8px; margin: 0; padding: 0; list-style: none; }
.nav li a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 12px; border-radius: 4px; text-decoration: none; color: var(--mute); font-size: .95rem; border-bottom: 2px solid transparent; }
.nav li a:hover { color: var(--ink); }
.nav li a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--pur-hi); }  /* The link for the page you are on gets a purple underline. */


/* ==========================================================================
   5. TYPOGRAPHY
   Headings, the small label above them (kicker), the intro text (lede)
   and the shared page spacing.
   ========================================================================== */
.kicker { font-family: var(--mono); font-size: .8rem; color: var(--pur-hi); letter-spacing: .06em; margin: 0 0 14px; }
h1 { margin: 0; font-family: var(--display); font-weight: 800; font-size: clamp(2.5rem, 8vw, 5rem); line-height: .98; text-transform: uppercase; letter-spacing: .01em; text-wrap: balance; max-width: 16ch; color: #fff; }  /* clamp() lets the size grow and shrink with the screen width, within a minimum and a maximum. */
h1 em { font-style: normal; color: var(--pur-hi); }  /* Words wrapped in <em> inside a heading are coloured purple instead of italic. */
h1 em.line { display: block; }                       /* Puts that purple part on its own line. */
h2 { margin: 0; font-family: var(--display); font-weight: 700; font-size: 1.9rem; line-height: 1.05; text-transform: uppercase; letter-spacing: .02em; }
h3 { margin: 0; font-family: var(--display); font-weight: 700; font-size: 1.6rem; line-height: 1.05; text-transform: uppercase; letter-spacing: .02em; }
.lede { max-width: 58ch; margin: 18px 0 0; color: var(--mute); font-size: 1.05rem; }
.project-rotator {
  margin-top: 10px;
  margin-bottom: 22px;
  min-height: 1.45em;   /* Keeps the cards from jumping when a short line swaps in */
  color: var(--pur-hi);
  text-shadow:
    0 0 8px rgba(217,200,255,.95),
    0 0 18px rgba(177,153,255,.85),
    0 0 36px rgba(106,47,208,.7),
    0 0 64px rgba(106,47,208,.45);
  transition: opacity .28s ease;
}
.project-rotator.is-out { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .project-rotator { transition: none; }
}
.label { font-family: var(--mono); font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--mute); margin: 0; }

/* Page spacing. Every direct child of a page gets 28px of space above it
   (the "> * + *" part means "any child that follows another child").
   The three rules after it then fix the few spots where that is too much or too little. */
.page { padding-top: 48px; }
.page > * + * { margin-top: 28px; }
.page > h1 { margin-top: 0; }
.page > .lede { margin-top: 18px; }
.page > .kicker + h1 { margin-top: 0; }
h1:focus { outline: none; }  /* The page title gets focus when a page opens (for screen readers). This hides the ring on it, since it is not something to click. */


/* ==========================================================================
   6. "HERE AS" MENU (THE DOOR)
   The row of buttons: Employer, Client, Colleague, Friend. The chosen one
   is filled with purple. It only reorders the cards.
   ========================================================================== */
.door { padding: 20px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); display: grid; gap: 12px; }
.row { display: flex; flex-wrap: wrap; gap: 10px; }
.row button { min-height: 44px; padding: 0 20px; border-radius: 6px; border: 1px solid var(--line); background: var(--panel); font-weight: 600; transition: background .2s, border-color .2s; }
.row button:hover { border-color: var(--pur-hi); }
.row button[aria-pressed="true"] { background: var(--pur); border-color: var(--pur-hi); color: #fff; }  /* "Pressed" is the selected button. */
#greet { margin: 0; }


/* ==========================================================================
   7. FLIP CARDS
   Cards with a front and a back. Pressing a card turns it over in 3D.
   Both sides sit in the same grid cell, stacked on top of each other.
   The back starts turned away (rotateY 180deg) and backface-visibility hides
   whichever side is facing away, so only one side shows at a time.
   ========================================================================== */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; }  /* As many columns as fit, each at least 250px wide. */
.card { position: relative; display: grid; padding: 0; border: 0; background: none; text-align: left; perspective: 900px; min-height: 200px; }  /* "perspective" gives the flip its 3D depth. */
.face { grid-area: 1 / 1; padding: 18px; border-radius: 8px; border: 1px solid var(--line); background: var(--panel); backface-visibility: hidden; transition: transform .6s cubic-bezier(.3,.7,.2,1); display: flex; flex-direction: column; gap: 10px; }
.back { transform: rotateY(180deg); border-color: var(--pur-hi); }
.card[aria-pressed="true"] .front { transform: rotateY(-180deg); }  /* When the card is pressed, the front turns away... */
.card[aria-pressed="true"] .back { transform: rotateY(0); }         /* ...and the back turns to face you. */
.face p { margin: 0; color: var(--mute); font-size: .9rem; line-height: 1.5; }
.face .hint { margin-top: auto; font-family: var(--mono); font-size: .74rem; color: var(--pur-hi); }  /* "margin-top: auto" pushes the hint to the bottom of the card. */
.back strong { color: var(--ink); }
.ph { color: var(--pur-hi); }  /* Highlights placeholder text, so unfinished spots are easy to see. */


/* ==========================================================================
   8. FRIEND EASTER EGG AND THE CARD SPHERE
   First the box shown when "Friend" is picked (a screenshot and a Play
   button). Then the floating sphere with the nine cards around it.
   ========================================================================== */

/* The Friend box. Dashed purple border, picture on the left, text on the right. */
.friend-door { display: flex; align-items: center; gap: 28px; margin: 28px 0; padding: 22px; border: 1px dashed var(--pur-hi); border-radius: 8px; background: var(--panel); }
.friend-door[hidden] { display: none; }
.fd-shot { flex: 0 1 380px; min-width: 0; width: 100%; max-width: 380px; height: auto; border: 3px solid #2a2150; border-radius: 4px; image-rendering: pixelated; }  /* "pixelated" keeps the pixel-art screenshot sharp when it is scaled up. */
.fd-text { flex: 1; display: grid; gap: 18px; justify-items: start; }
.fd-line { margin: 0; font-family: var(--mono); font-size: .9rem; color: var(--mute); }
@media (max-width: 720px) { .friend-door { flex-direction: column; } .fd-shot { max-width: 100%; flex-basis: auto; } .fd-text { justify-items: center; text-align: center; } }  /* On narrow screens the picture goes above the text. */
.fd-btn { display: inline-block; padding: 14px 28px; border: 1px solid var(--pur-hi); border-radius: 6px; background: rgba(139,108,255,.14); color: var(--ink); font-family: var(--mono); font-size: 1.05rem; font-weight: 600; text-decoration: none; }
.fd-btn:hover, .fd-btn:focus-visible { background: rgba(139,108,255,.3); }

/* The sphere area. #orb is the wireframe sphere, drawn on a canvas that fills
   the stage. The cards (.tile) are placed on top of it by the script, which
   sets each tile's position and size, so they are only anchored here at the top left. */
.stage3d { position: relative; width: min(100%, 720px); aspect-ratio: 1; margin: 0 auto; }  /* A square area, at most 720px wide. */
#orb { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.tile { position: absolute; left: 0; top: 0; width: 126px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px; background: rgba(23,17,34,.94); cursor: pointer; will-change: transform, opacity; transition: width .28s cubic-bezier(.3,.7,.2,1), border-color .2s, box-shadow .3s; }  /* "will-change" warns the browser that tiles move constantly, so it can prepare. */

/* Blueprint drawings on each card. Each one is a small SVG using the classes below. */
.art { margin: 0 0 10px; border: 1px solid var(--line); border-radius: 4px; overflow: hidden; background-color: rgba(10,7,16,.65); background-image: linear-gradient(rgba(139,108,255,.12) 1px, transparent 1px), linear-gradient(90deg, rgba(139,108,255,.12) 1px, transparent 1px); background-size: 14px 14px; }
.art svg { display: block; width: 100%; height: auto; }
.a-line { fill: none; stroke: var(--pur-hi); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }  /* A normal drawn line. */
.a-dim { stroke: #6f5fa6; }                                                                                     /* A dimmer line, for background detail. */
.a-node { fill: #171122; stroke: var(--pur-hi); stroke-width: 1.5; }                                           /* A dark dot or box in the drawing. */
.a-hot { fill: var(--pur); stroke: #d6bfff; stroke-width: 1.5; }                                               /* A highlighted, purple-filled part. */
.a-flow { stroke-dasharray: 4 4; animation: flowA 1.2s linear infinite; }                                      /* A dashed line whose dashes move along it, like data flowing. */
.a-pulse { animation: pulseA 2.4s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }   /* A part that gently grows and shrinks. The two lines make it scale from its own centre. */
@keyframes flowA { to { stroke-dashoffset: -8; } }  /* Slides the dashes by one dash-and-gap (8px), then repeats. */
@keyframes pulseA { 50% { transform: scale(1.08); } }

/* Inside a card. The text starts collapsed and opens when the card is chosen.
   "grid-template-rows: 0fr" collapses it to nothing, and "1fr" opens it to full height,
   which lets the height animate smoothly without knowing the text's size in advance. */
.tile h3 { font-size: 1.05rem; }
.tile .full { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .3s cubic-bezier(.3,.7,.2,1); }
.tile .full > div { overflow: hidden; min-height: 0; }
.tile p { margin: 8px 0 0; color: var(--mute); font-size: .9rem; line-height: 1.5; }
.tile p.sub { margin-top: 14px; font-family: var(--mono); font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--pur-hi); }
.tile strong { color: var(--ink); }
.tile.open { width: min(310px, calc(100% - 12px)); border-color: var(--pur-hi); box-shadow: 0 0 0 1px var(--pur-hi), 0 0 36px var(--glow), 0 16px 44px rgba(0,0,0,.65); cursor: default; }  /* An opened card is wider, outlined in purple and lifted with a shadow. */
.tile.open .full { grid-template-rows: 1fr; }
.tile:focus-visible { outline: 2px solid var(--pur-hi); outline-offset: 3px; }
.stage-hint { margin: 10px 0 0; text-align: center; font-family: var(--mono); font-size: .75rem; color: var(--mute); }
@media (max-width: 480px) { .tile { width: 104px; padding: 8px 10px; } .tile h3 { font-size: .95rem; } }  /* Smaller cards on phones, so the sphere still fits. */


/* ==========================================================================
   9. LAYER PICKER
   A column of seven aluminium buttons (one per layer) with the text for the
   chosen layer beside it. On narrow screens they stack on top of each other.
   ========================================================================== */
.stack { display: grid; grid-template-columns: minmax(200px, 300px) 1fr; gap: 20px; align-items: start; }
.layers { display: grid; gap: 6px; }
.layer { min-height: 44px; padding: 8px 14px; text-align: left; border: 0; border-radius: 4px; background: var(--brush), var(--alu); color: #1b1722; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 0 rgba(0,0,0,.5); }  /* A brushed-aluminium button with a light top edge and a dark bottom edge, so it looks raised. */
.layer[aria-pressed="true"] { background: var(--pur); color: #fff; box-shadow: 0 0 0 2px var(--pur-hi), 0 0 24px var(--glow); }  /* The chosen layer turns purple and glows. */
.layer-detail { padding: 22px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); display: grid; gap: 10px; min-height: 180px; align-content: start; }
.layer-detail p { margin: 0; color: var(--mute); }
@media (max-width: 640px) { .stack { grid-template-columns: 1fr; } }


/* ==========================================================================
   10. PEEK STRIP
   A thin terminal-style bar with a blinking cursor. It is a link
   that gives a glimpse of the shell.
   ========================================================================== */
.peek { display: flex; align-items: center; gap: 10px; min-height: 52px; padding: 0 16px; border: 1px solid var(--pur-hi); border-radius: 8px; background: #0a0710; font-family: var(--mono); font-size: .88rem; text-decoration: none; }
.peek .p { color: var(--pur-hi); }  /* The prompt text. */
.peek .c { width: 8px; height: 1.1em; background: var(--pur-hi); animation: blink 1.1s steps(1) infinite; }  /* The cursor block. "steps(1)" makes it snap on and off instead of fading. */
.peek .t { margin-left: auto; color: var(--mute); }  /* Pushes this text to the far right. */
@keyframes blink { 50% { opacity: 0; } }


/* ==========================================================================
   11. VALUES PAGE
   Five numbered principles, one card each: a big number on the left and
   the text on the right.
   ========================================================================== */
.principles { display: grid; gap: 14px; }
.principle { display: grid; grid-template-columns: 56px 1fr; gap: 16px; padding: 20px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
.principle .n { font-family: var(--display); font-weight: 800; font-size: 2.6rem; line-height: 1; color: var(--pur-hi); }
.principle h2 { font-size: 1.5rem; margin-bottom: 6px; }
.principle p { margin: 0; color: var(--mute); max-width: 60ch; }
.draft { display: inline-block; padding: 4px 10px; border: 1px dashed var(--pur-hi); border-radius: 4px; font-family: var(--mono); font-size: .76rem; color: var(--pur-hi); }  /* A small dashed badge, used to mark something as a draft. */


/* ==========================================================================
   12. PROJECTS PAGE
   Stacked cards: shot on the left, copy on the right. Same height for all
   three. Full start-page images (contain). Hover lifts + purple backglow.
   (Class name "repo" is leftover from when this was the GitHub page.)
   ========================================================================== */
.project-stack { display: flex; flex-direction: column; gap: 18px; }
.repo {
  display: grid;
  grid-template-columns: 300px 1fr;
  align-items: stretch;
  min-height: 280px;  /* Match TerminalPro; shorter cards stretch to the same height */
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  overflow: hidden;
  transform: translateZ(0);
  transition: transform .28s cubic-bezier(.3,.7,.2,1), box-shadow .28s ease, border-color .28s ease;
}
.repo:hover, .repo:focus-within {
  transform: translateY(-8px) scale(1.012);
  border-color: var(--pur-hi);
  box-shadow:
    0 0 0 1px rgba(177,153,255,.35),
    0 0 36px rgba(106,47,208,.5),
    0 22px 48px rgba(0,0,0,.55);
  z-index: 1;
}
.repo-media {
  background: #0a0710;
  border-right: 1px solid var(--line);
}
.repo-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;   /* Whole start page visible */
  object-position: center;
}
.repo-body { display: flex; flex-direction: column; gap: 10px; padding: 20px 22px; }
.repo h3 { margin: 0; font-size: 1.25rem; }
.repo p { margin: 0; color: var(--mute); font-size: .95rem; }
.chips { margin-top: auto; display: flex; gap: 8px; flex-wrap: wrap; font-family: var(--mono); font-size: .74rem; }
.chips span { padding: 2px 10px; border: 1px solid var(--line); border-radius: 99px; color: var(--mute); }  /* A radius of 99px is just "as round as possible", which makes a pill shape. */
@media (max-width: 640px) {
  .repo { grid-template-columns: 1fr; }
  .repo-media { min-height: 160px; aspect-ratio: 16 / 9; border-right: 0; border-bottom: 1px solid var(--line); }
}


/* ==========================================================================
   13. PAULSHELL (THE FAKE POWERSHELL)
   A terminal window: an aluminium title bar, a scrolling output area
   and a prompt line at the bottom.
   ========================================================================== */
.shell { border-radius: 8px; border: 1px solid var(--pur-hi); background: #0a0710; box-shadow: 0 0 0 4px rgba(106,47,208,.18), 0 0 40px var(--glow); overflow: hidden; }
.shell-head { display: flex; align-items: center; gap: 8px; padding: 8px 14px; background: var(--brush), var(--alu); color: #1b1722; font-family: var(--mono); font-size: .76rem; font-weight: 500; }
.shell-head i { width: 9px; height: 9px; border-radius: 50%; background: #5d6270; display: inline-block; }  /* The small round dots in the title bar. */

/* The output area. Fixed height, and it scrolls when the text gets longer. */
#out { height: 400px; overflow-y: auto; padding: 14px; font-family: var(--mono); font-size: .86rem; line-height: 1.5; }
.ln { white-space: pre-wrap; overflow-wrap: anywhere; color: #e4def2; }  /* One line of output. "pre-wrap" keeps the spacing, and long words are allowed to break. */
.ln.dim { color: #9b93b2; }             /* Quiet output. */
.ln.hi { color: var(--pur-hi); }        /* Highlighted output. */
.ln.cmd { color: #fff; }                /* The command you typed. */

/* Pictures the shell can print: the banana and the wrench. Both are pixel art
   and are animated in steps, which gives a chunky, old-game feel. */
.sprite { display: block; margin: 8px 0; image-rendering: pixelated; }
.banana { width: 200px; animation: bob 1s steps(2) infinite; }
.banana.slip { animation: slip .9s steps(9) forwards; }  /* The slip gag: it falls over and fades. "forwards" keeps the final pose. */
.wrench { width: 112px; animation: tink .5s steps(2) infinite; }
@keyframes bob { 50% { transform: translateY(-4px); } }
@keyframes slip { to { transform: translate(40px, 8px) rotate(95deg) scale(.55); opacity: .6; } }
@keyframes tink { 50% { transform: rotate(-8deg); } }

/* The prompt line where you type. */
.prompt-row { display: flex; align-items: center; gap: 8px; padding: 0 14px 14px; font-family: var(--mono); font-size: .86rem; }
.prompt-row label { color: var(--pur-hi); white-space: nowrap; }
#cmd { flex: 1; min-width: 0; min-height: 44px; background: transparent; border: 0; border-bottom: 1px solid #3d3160; color: #fff; font: inherit; caret-color: var(--pur-hi); }  /* No box, only an underline. "caret-color" is the blinking text cursor. */
#cmd:focus { outline: none; border-bottom-color: var(--pur-hi); }  /* The underline turns purple instead of showing the default ring. */

.foot { margin: 40px 0 0; padding-top: 16px; border-top: 1px solid var(--line); font-size: .85rem; color: var(--mute); }  /* The footer text at the bottom of the page. */


/* ==========================================================================
   14. PAGE TRANSITIONS AND TOAST
   #fx is a see-through layer over the whole screen (but cannot be clicked).
   The two plates are the shutters. They close from the top and bottom, meet at
   the seam, then open again to show the new page. The scan is the sweep that
   moves across the page. The toast is the small message at the bottom.
   ========================================================================== */
#fx { position: fixed; inset: 0; z-index: 50; pointer-events: none; overflow: hidden; }
.plate { position: fixed; left: 0; right: 0; height: 50.5%; background: var(--brush), var(--alu); }  /* Each plate is a little over half the screen, so the two overlap and leave no gap when closed. */
.plate::after { content: ""; position: absolute; left: 0; right: 0; height: 14px; background: radial-gradient(circle closest-side, #3b3f46 0 28%, #d4d7dc 32% 60%, transparent 64%) 0 0 / 28px 14px repeat-x; }  /* A row of bolts along the plate's inner edge. */
.plate.top { top: 0; box-shadow: 0 6px 24px rgba(0,0,0,.5); }
.plate.top::after { bottom: 10px; }
.plate.bot { bottom: 0; box-shadow: 0 -6px 24px rgba(0,0,0,.5); }
.plate.bot::after { top: 10px; }
.seam { position: fixed; left: 0; right: 0; top: calc(50% - 2px); height: 4px; background: #c9b3ff; box-shadow: 0 0 28px 8px #7a46e8; opacity: 0; }  /* The glowing line where the plates meet. Invisible until the script fades it in. */
.scan { position: absolute; inset: 0; z-index: 30; background-color: #120a2a; background-image: linear-gradient(rgba(165,139,255,.2) 1px, transparent 1px), linear-gradient(90deg, rgba(165,139,255,.2) 1px, transparent 1px); background-size: 32px 32px; border-right: 3px solid #c9b3ff; box-shadow: 14px 0 50px 6px rgba(122,70,232,.7); transform: translateX(-100%); pointer-events: none; }  /* Starts fully off the left edge. The script slides it across. */
.burst { position: fixed; inset: 0; }
#toast { position: fixed; left: 50%; bottom: 24px; z-index: 60; transform: translate(-50%, 20px); opacity: 0; padding: 10px 18px; border-radius: 6px; background: #17122a; border: 1px solid var(--pur-hi); font-family: var(--mono); font-size: .85rem; transition: opacity .3s, transform .3s; pointer-events: none; }  /* Hidden and sitting 20px low until it is switched on. */
#toast.on { opacity: 1; transform: translate(-50%, 0); }  /* "On" fades it in and slides it up into place. */


/* ==========================================================================
   15. ACCESSIBILITY AND SMALL SCREENS
   ========================================================================== */

/* Some people set their device to reduce motion. For them, this turns off the
   card flips, the toast slide and all the looping animations. */
@media (prefers-reduced-motion: reduce) { .face, #toast { transition: none; } .peek .c { animation: none; } .banana, .wrench, .banana.slip, .a-flow, .a-pulse { animation: none; } }

/* On phones: a thinner frame, a shorter shell and a single-column principle. */
@media (max-width: 480px) { :root { --frame: 10px; } #out { height: 320px; } .principle { grid-template-columns: 1fr; gap: 6px; } }


/* ==========================================================================
   16. GAME PAGE (OPEY.EXE)
   The title, the game screen and the touch controls for phones.
   The game itself is drawn on a canvas by game.js. This is only its frame.
   ========================================================================== */
#p-play .gkick { margin: 0; }
#h-play { font-size: clamp(28px, 4.2vw, 46px); line-height: 1; margin-top: 8px; }
#gamepage { margin: 18px -34px 0; }  /* The negative side margins let the game screen reach a little past the page padding, for a bigger play area. */
@media (max-width: 1100px) { #gamepage { margin-left: 0; margin-right: 0; } }  /* On smaller screens there is no room to spare, so the extra width is removed. */

/* The game screen: always 16:9, as wide as fits while staying inside the window height.
   "touch-action: none" and "user-select: none" stop the page from scrolling or
   selecting text while someone plays by touch. */
#g-wrap { position: relative; width: min(100%, calc((100vh - 190px) * 16 / 9)); min-width: 300px; margin: 0 auto; aspect-ratio: 16 / 9; border: 6px solid #2a2140; border-radius: 8px; box-shadow: 0 0 0 2px #6a2fd0, 0 0 40px rgba(106, 47, 208, .3); background: #0b0812; touch-action: none; -webkit-user-select: none; user-select: none; }
#g-wrap canvas { display: block; width: 100%; height: 100%; image-rendering: pixelated; image-rendering: crisp-edges; }  /* The second image-rendering line is a fallback for browsers that do not know "pixelated". */
#g-wrap canvas#g-t { position: absolute; left: 0; top: 0; pointer-events: none; image-rendering: auto; }  /* The text canvas sits on top of the game canvas. It is drawn at higher resolution, so it stays smooth. */

/* Full screen on phones. The button only shows on touch devices ("pointer: coarse"). */
#g-fs { display: none; position: absolute; top: 8px; right: 8px; z-index: 5; padding: 6px 10px; border-radius: 5px; border: 1px solid #b199ff; background: rgba(18, 10, 42, .78); color: #e9e2ff; font: 600 11px var(--mono); letter-spacing: .06em; text-transform: uppercase; }
@media (pointer: coarse) { #g-fs { display: block; } }
#g-fsbg { position: fixed; inset: 0; z-index: 400; background: #05020c; touch-action: none; }  /* A dark backdrop that covers the whole screen in full screen mode. */
#g-wrap.fs { position: fixed; margin: 0; border: 0; border-radius: 0; box-shadow: none; min-width: 0; max-width: none; }
#g-wrap.fs #g-fs { display: none; }  /* In full screen, the "enter full screen" button is hidden. */
#g-fsx { position: absolute; top: 8px; right: 8px; z-index: 410; width: 34px; height: 34px; border-radius: 50%; border: 1px solid rgba(177, 153, 255, .7); background: rgba(18, 10, 42, .6); color: #e9e2ff; font: 700 15px var(--mono); line-height: 1; opacity: .75; }  /* The round X for leaving full screen. */
#g-fsmsg { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; flex-direction: column; gap: 14px; padding: 24px; text-align: center; color: #e9e2ff; font: 600 14px/1.5 var(--mono); letter-spacing: .05em; }  /* A message over the game, hidden until the script needs it. */
#g-fsmsg b { font-size: 34px; display: block; }

/* Touch controls. They only show on touch devices while a game is running. */
#g-tb { display: none; }
@media (pointer: coarse) { #g-wrap.playing #g-tb { display: block; } }
#g-wrap.touch.playing #g-tb { display: block; }
#g-wrap.paused .gbtn:not(#g-bpause) { display: none; }  /* While paused, every touch button hides except the pause button. */
#g-hint { display: none; margin: 10px 0 0; text-align: center; color: var(--mute); font: 500 12px var(--mono); letter-spacing: .06em; }
@media (orientation: portrait) and (pointer: coarse) { #g-hint { display: block; } }  /* Shows the hint only on a phone held upright. */
#g-wrap .gbtn { position: absolute; bottom: 5%; width: min(17%, 24vh); aspect-ratio: 1; border-radius: 50%; border: 3px solid #b199ff; background: rgba(106, 47, 208, .35); color: #fff; font: 700 clamp(9px, 2.2vw, 16px) 'IBM Plex Mono', monospace; letter-spacing: .06em; display: flex; align-items: center; justify-content: center; text-align: center; touch-action: none; }  /* The round touch buttons. */
#g-wrap .gbtn.on { background: rgba(177, 153, 255, .55); }  /* Lights up while a finger is on it. */
#g-bw { right: 21%; }                                        /* The whip button. */
#g-bp { right: 2%; bottom: 14%; }                            /* The packet button. */
#g-bpause { right: 2%; top: 13%; bottom: auto !important; width: 8% !important; border-width: 2px !important; background: rgba(11, 8, 18, .6) !important; }  /* The pause button: smaller, at the top right. The "!important" parts override the shared button style above. */
.plink { color: var(--pur-hi); text-decoration: underline; text-underline-offset: 3px; font-weight: 600; }  /* The purple underlined "Play it" links inside cards. */
