/* ═══════════════════════════════════════════════════════════════
   emilgigov.com — v0.6
   Windows 95 / Netscape Navigator, two ways.

   LAWS
   1. Light is white windows on the classic Windows desktop blue.
      Dark is the same chrome with the silver taken down to near-black.
      Never grey windows.
   2. TEXT IS PLAIN. White in dark, near-black in light. Body copy,
      headings, table cells, labels — all of it. The only coloured word
      on the page is "GIGOV" (the accent blue) and links. Colour lives
      in chrome.
   3. Blue title bars in both themes. The Win95 navy gradient is the
      one signature element and it survives the invert.
   4. Real 2px Win95 bevels, plus a 1px outline on every box.
      No radius. No blur. No soft shadows.
   5. The desktop behind the windows carries an 80s Memphis pattern —
      stars, rings, cubes, stripes, zigzags. Lighter blue on the light
      desktop; contrast-dark shapes on the dark one.
   6. Space to breathe. Generous padding, long line-height, >=44px targets.
   7. Both themes are audited to WCAG AA on every text element, with the
      title-bar gradient measured at its lightest stop.
   ═══════════════════════════════════════════════════════════════ */

/* ── DARK (default) — the light theme below, with the silver pushed to black ── */
:root{
  --bg:#04070D;        /* desktop: near-black blue, carries the pattern */
  --pattern:url('/assets/img/pattern-dark.svg');
  --face:#0E0E0E;      /* window face — Win95's #C0C0C0, darkened to near-black */
  --face-2:#161616;    /* nested panel / row hover */
  --well:#070707;      /* sunken: inputs, lists, wells */

  --ink:#FFFFFF;       /* body text. white. */
  --ink-2:#C6C6C6;     /* secondary */
  --ink-3:#8A8A8A;     /* faint / disabled */

  --edge:#454545;      /* the outline every box carries */

  --blue:#4D9BFF;      /* the accent */
  --blue-2:#1B5FA8;    /* Win95 title gradient, light stop. Darkened from the
                          authentic #1084D0: white title ink was 3.1:1 there. */
  --navy:#000E5C;      /* Win95 title gradient, dark stop */
  --sel:#0A2C8C;       /* menu / list selection */
  --sel-ink:#FFFFFF;

  /* Win95 bevel ramp, darkened. Neutral grey — never tinted. */
  --bev-hi:#6E6E6E;    /* inner top-left     (Win95 #FFFFFF) */
  --bev-hi2:#3A3A3A;   /* outer top-left     (Win95 #DFDFDF) */
  --bev-lo2:#242424;   /* outer bottom-right (Win95 #808080) */
  --bev-lo:#000000;    /* inner bottom-right (Win95 #000000) */

  --ttl:linear-gradient(90deg,var(--navy) 0%,var(--blue-2) 100%);
  --ttl-ink:#FFFFFF;
  --ttl-idle:#1A1A1A;
  --ttl-idle-ink:#9A9A9A;

  --drop:#000000;      /* solid drop shadow */

  /* --f-ui is the PIXEL face and is now display-only: .h1, .doc-title, .doc-h,
     and nothing else. Silkscreen is a bitmap drawn on an 8px grid, so it only
     rasterises cleanly at 8/16/24; the site was using it at 7,8,9,10,11,15 with
     fractional letter-spacing, which put every stem and every glyph origin
     between device pixels. On a dense or fractionally-scaled monitor that reads
     as uneven, mushy and — at 9-10px — barely legible.

     --f-chrome carries all the functional chrome instead. It is an outline
     font, so it is crisp at any size and any DPI. It is uppercased wherever it
     replaced Silkscreen, because Silkscreen draws its lowercase as small caps,
     which is why the chrome reads as uppercase today. */
  --f-ui:'Silkscreen','Chicago','Geneva',sans-serif;
  --f-chrome:'IBM Plex Mono','Courier New',monospace;
  --f-body:'IBM Plex Mono','Courier New',monospace;
  --f-prose:'Times New Roman',Times,Georgia,serif;
  --f-display:'VT323','Silkscreen',monospace;

  --s1:4px;--s2:8px;--s3:14px;--s4:20px;--s5:28px;--s6:40px;--s7:56px;

  /* One source of truth for where a section lands under the sticky rail.
     scroll-margin-top and the JS scroller both read it, so a click, a
     deep link and the browser's own fragment scroll all agree. */
  --nav-offset:20px;
}

/* ── LIGHT — white windows on the blue desktop. No grey. ── */
html[data-theme="light"]{
  --bg:#3A6EA5;        /* the classic Windows desktop blue, patterned */
  --pattern:url('/assets/img/pattern-light.svg');
  --face:#FFFFFF;      /* windows are white */
  --face-2:#EDF3FC;    /* nested panel / row hover */
  --well:#F2F7FD;      /* sunken: inputs, lists, wells */

  --ink:#0B1B33;
  --ink-2:#3C5A80;
  --ink-3:#566F92;

  --edge:#0A246A;      /* navy outline on every box */

  --blue:#0A3D91;
  --blue-2:#1B5FA8;
  --navy:#000080;
  --sel:#0A3D91;
  --sel-ink:#FFFFFF;

  --bev-hi:#FFFFFF;
  --bev-hi2:#DCE7F5;
  --bev-lo2:#8FA9C7;
  --bev-lo:#0A246A;

  --ttl:linear-gradient(90deg,#000080 0%,#1B5FA8 100%);
  --ttl-ink:#FFFFFF;
  --ttl-idle:#C9D8EC;
  --ttl-idle-ink:#3C5A80;

  --drop:rgba(4,20,48,.42);
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scrollbar-color:var(--bev-lo2) var(--well)}
body{
  background:var(--bg);
  color:var(--ink);
  font-family:var(--f-body);font-size:15px;line-height:1.7;
  overscroll-behavior:none;
}

/* Antialiasing is switched OFF only for the pixel face. It was previously off
   for the whole document, which is correct for a bitmap font and actively wrong
   for an outline one — it would render the chrome jagged and unsmoothed. So it
   is scoped to exactly the elements still set in Silkscreen. */
.h1,.doc-title,.doc-h,.tbar,.win-title,.h2,.lbl,.btn88{-webkit-font-smoothing:none;font-smooth:never}
body.locked{overflow:hidden}
img{image-rendering:pixelated;max-width:100%}
a{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
a:visited{color:var(--ink-2)}
a:hover{background:var(--sel);color:var(--sel-ink);text-decoration:none}
code{font-family:var(--f-body);background:var(--well);color:var(--ink);padding:2px 6px}
button{font:inherit}

/* ── Outline on every box. Bevel inside it, edge around it. ── */
.win,.dlg,.mdrop,.menubar,.toolbar,.viewport,.btn,.tb,.wb,.input,.urlwrap,.throbber,
.tree,.pane,.shot,.shot .img,.skill,.pill,.tag,.btn88,.exif div,.crumb,.meter,.counter,
.counter b,.dlg-icon,.dicon .glyph,.sbar>div,.win-status>div,.viewer .shotbig,.construction,
.construction>span,.np-menu,.fig,.fig .img,.term-block,.chart-bar,.pull,.doc-nav,
.tl-card,.tl-ico,.tl-node{
  border:1px solid var(--edge);
}

/* ── Win95 bevel primitives ── */
.raised,.btn,.tb,.wb,.win,.toolbar,.menubar,.mdrop,.dlg,.skill,.pill,.tag,.btn88,.exif div{
  box-shadow:
    inset  1px  1px 0 var(--bev-hi2),
    inset -1px -1px 0 var(--bev-lo),
    inset  2px  2px 0 var(--bev-hi),
    inset -2px -2px 0 var(--bev-lo2);
}
.sunken,.input,.urlwrap,.viewport,.throbber,.well,.tree,.pane,.crumb,.meter,.counter,
.sbar>div,.win-status>div,.shot .img,.viewer .shotbig,.dlg-icon,
.fig .img,.term-block,.chart-bar,.tl-ico{
  box-shadow:
    inset  1px  1px 0 var(--bev-lo2),
    inset -1px -1px 0 var(--bev-hi2),
    inset  2px  2px 0 var(--bev-lo),
    inset -2px -2px 0 var(--bev-hi);
}
.pressed,.btn:active,.tb:active,.wb:active{
  box-shadow:
    inset  1px  1px 0 var(--bev-lo),
    inset -1px -1px 0 var(--bev-hi2),
    inset  2px  2px 0 var(--bev-lo2),
    inset -2px -2px 0 var(--bev-hi);
}

/* Win95 focus: the dotted marching rectangle, inset one pixel */
:focus-visible{outline:1px dotted var(--ink);outline-offset:2px}
.btn:focus-visible,.tb:focus-visible,.mtrig:focus-visible,.mitem:focus-visible,
.tnode:focus-visible,.shot:focus-visible,.wb:focus-visible{outline:1px dotted currentColor;outline-offset:-5px}

::-webkit-scrollbar{width:17px;height:17px}
::-webkit-scrollbar-track{background:var(--well)}
::-webkit-scrollbar-thumb{background:var(--face);
  box-shadow:inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo),
             inset 2px 2px 0 var(--bev-hi),inset -2px -2px 0 var(--bev-lo2)}
::selection{background:var(--sel);color:var(--sel-ink)}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ═══ BROWSER SHELL ═══ */
.browser{position:fixed;inset:0;height:100dvh;display:flex;flex-direction:column;
  background:var(--face);padding:3px;gap:3px}
.tbar{background:var(--ttl);color:var(--ttl-ink);display:flex;align-items:center;gap:var(--s2);flex:none;
  padding:6px 6px 6px 9px;font-family:var(--f-ui);text-transform:uppercase;font-size:16px;letter-spacing:1px;user-select:none}
.tbar .dot{width:9px;height:9px;background:var(--ttl-ink);flex:none}
.tbar .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wb{width:22px;height:20px;background:var(--face);color:var(--ink);cursor:pointer;
  font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;line-height:1;text-align:center;padding:0;flex:none}

/* ── MENU BAR ── */
.menubar{background:var(--face);display:flex;flex:none;position:relative;z-index:60;
  padding:2px;font-family:var(--f-chrome);text-transform:uppercase;font-size:13px}
.menubar>div{position:relative}
.mtrig{background:none;border:0;color:var(--ink);padding:8px 11px;cursor:pointer;
  font-family:var(--f-chrome);text-transform:uppercase;font-size:13px;white-space:nowrap}
.mtrig:hover,.mtrig[aria-expanded="true"]{background:var(--sel);color:var(--sel-ink)}
.mdrop{position:absolute;top:100%;left:0;min-width:232px;background:var(--face);display:none;z-index:70;
  padding:3px;filter:drop-shadow(4px 4px 0 var(--drop))}
.mdrop.open{display:block}
.mitem{display:flex;width:100%;align-items:center;gap:var(--s3);background:none;border:0;cursor:pointer;
  color:var(--ink);font-family:var(--f-chrome);text-transform:uppercase;
  font-size:12px;text-align:left;padding:11px 12px;line-height:1.5}
.mitem:hover{background:var(--sel);color:var(--sel-ink)}
.mitem .k{margin-left:auto;color:var(--ink-3);font-size:10px;padding-left:var(--s4)}
.mitem:hover .k{color:var(--sel-ink)}
.mitem .tick{width:10px;flex:none;color:var(--ink)}
.mitem:hover .tick{color:var(--sel-ink)}
.msep{height:2px;margin:4px 3px;border-top:1px solid var(--bev-lo2);border-bottom:1px solid var(--bev-hi2)}

/* ── TOOLBAR + URL BAR ── */
.toolbar{background:var(--face);display:flex;align-items:center;gap:6px;padding:6px;flex:none}
.tb{background:var(--face);color:var(--ink);cursor:pointer;flex:none;
  font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;padding:0 12px;min-height:36px;white-space:nowrap}
.tb[disabled]{color:var(--ink-3);cursor:not-allowed}
.urlwrap{flex:1;display:flex;align-items:center;gap:var(--s3);background:var(--well);min-width:0;
  padding:0 10px;min-height:36px}
.urlwrap .loc{font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;color:var(--ink-3);flex:none}
#url{flex:1;min-width:0;background:none;border:0;outline:0;color:var(--ink);
  font-family:var(--f-body);font-size:16px;padding:8px 0}
#url:focus{color:var(--ink)}

/* throbber — stepped block, no easing */
.throbber{width:28px;height:28px;flex:none;background:var(--well);position:relative;overflow:hidden}
.throbber::after{content:'';position:absolute;left:5px;top:5px;width:6px;height:6px;
  background:var(--ink-3);animation:march 2.4s steps(4) infinite}
.throbber.busy::after{background:var(--blue);animation-duration:.5s}
@keyframes march{
  0%{transform:translate(0,0)} 25%{transform:translate(12px,0)}
  50%{transform:translate(12px,12px)} 75%{transform:translate(0,12px)}
  100%{transform:translate(0,0)}
}

/* ── VIEWPORT ── */
/* The desktop. Memphis pattern tiles behind the windows — light gets a lighter
   blue on the classic desktop blue, dark gets contrast-dark shapes on near-black. */
.viewport{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;
  background-color:var(--bg);background-image:var(--pattern);
  background-repeat:repeat;background-size:220px 220px;background-attachment:local;
  padding:var(--s5) var(--s4) var(--s7);position:relative}
.viewport.scanlines::after{content:'';position:fixed;inset:0;pointer-events:none;z-index:40;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,.4) 0 1px,transparent 1px 3px)}

.sbar{display:flex;gap:3px;flex:none;font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;color:var(--ink-2)}
.sbar>div{background:var(--face);padding:7px 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sbar>div:first-child{flex:1;min-width:0}
.sbar .ok{color:var(--ink)}

/* ═══ PAGE ═══ */
.page{max-width:1140px;margin:0 auto;display:grid;gap:var(--s4)}
@media(min-width:900px){.page{grid-template-columns:150px 1fr;align-items:start}}
.sidebar{display:flex;gap:var(--s2)}
@media(min-width:900px){.sidebar{flex-direction:column;position:sticky;top:0}}
.main{display:grid;gap:var(--s5);min-width:0}

/* Icon labels sit directly on the patterned desktop, not on a window face.
   White + a hard shadow, exactly as Win95 drew them — dark ink on the desktop
   blue only reached 3.2:1. */
.dicon{width:78px;flex:none;text-align:center;font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;
  color:#FFFFFF;text-shadow:1px 1px 0 rgba(0,0,0,.6);
  text-decoration:none;display:block;padding:8px 0;line-height:1.4}
.dicon:hover{background:none;color:#FFFFFF}
.dicon .glyph{width:44px;height:40px;margin:0 auto 8px;background:var(--face);
  display:flex;align-items:center;justify-content:center;font-size:19px;color:var(--ink);
  box-shadow:inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo),
             inset 2px 2px 0 var(--bev-hi),inset -2px -2px 0 var(--bev-lo2)}
.dicon:hover .glyph,.dicon.here .glyph{background:var(--sel);color:var(--sel-ink)}
.dicon .cap{padding:2px 4px;display:inline-block}
.dicon:hover .cap,.dicon.here .cap{background:var(--sel);color:var(--sel-ink)}

/* ═══ WINDOW ═══ */
/* min-width:0 because a grid item defaults to min-width:auto and so refuses to
   shrink below its min-content width. .main already carries it; without it here
   too, one wide child (a long window title, a data table) pushes the whole
   window past the screen instead of letting .tblwrap / ellipsis do their job. */
.win{background:var(--face);padding:3px;min-width:0;scroll-margin-top:var(--nav-offset)}
.win-title{background:var(--ttl);color:var(--ttl-ink);display:flex;align-items:center;gap:var(--s2);
  padding:6px 6px 6px 9px;font-family:var(--f-ui);text-transform:uppercase;font-size:16px;letter-spacing:1px;user-select:none}
.win-title.idle{background:var(--ttl-idle);color:var(--ttl-idle-ink)}
.win-title.idle .dot{background:var(--ttl-idle-ink)}
.win-title .dot{width:9px;height:9px;background:var(--ttl-ink);flex:none}
.win-title .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.win-title .btns{display:flex;gap:3px}
.win-body{padding:var(--s5)}
.win-body.tight{padding:var(--s4)}
.win-status{display:flex;gap:3px;margin-top:3px;font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;color:var(--ink-2)}
.win-status>div{background:var(--face);padding:7px 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.win-status>div:first-child{flex:1;min-width:0}
.win-status .ok{color:var(--ink)}

/* ═══ TYPE ═══ */
/* The wordmark is a hand-drawn 7x9 pixel grid, not type. Silkscreen is ~5x7,
   and at that resolution its M only drops one row in the middle, so "EMIL"
   read as "EHIL". Two extra rows of height buy M a three-row vertex.
   Width drives the size; height follows the 37:21 viewBox. */
.d1{line-height:0}
.d1 .wordmark{display:block;width:clamp(196px,32vw,286px);height:auto}
.wm-a{fill:var(--ink)}
.wm-b{fill:var(--blue)}   /* the one coloured word on the site */
.h1{font-family:var(--f-ui);font-size:16px;letter-spacing:.5px;color:var(--ink);font-weight:400;
  line-height:1.5}
.h2{font-family:var(--f-ui);text-transform:uppercase;font-size:16px;letter-spacing:1px;color:var(--ink);font-weight:400;
  line-height:1.6}
.lbl{font-family:var(--f-ui);font-size:16px;letter-spacing:1px;color:var(--ink-2);text-transform:uppercase;
  line-height:1.7}
.prose{font-family:var(--f-prose);font-size:18px;line-height:1.75;color:var(--ink);max-width:64ch}
.prose p+p{margin-top:1em}
.prose em{color:var(--ink);font-style:italic}
.term{font-family:var(--f-display);font-size:21px;line-height:1.5;color:var(--ink);word-break:break-word}
.note{font-size:13px;color:var(--ink-2);line-height:1.75}
.caret{display:inline-block;width:9px;height:1em;background:var(--ink);vertical-align:-2px;
  animation:blink 1.06s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
hr.rule{border:0;height:2px;margin:var(--s5) 0;
  border-top:1px solid var(--bev-lo2);border-bottom:1px solid var(--bev-hi2)}

/* ═══ CONTROLS ═══ */
.btn{display:inline-flex;align-items:center;justify-content:center;
  background:var(--face);color:var(--ink);cursor:pointer;
  font-family:var(--f-chrome);text-transform:uppercase;font-size:13px;letter-spacing:0.2px;padding:0 20px;min-height:44px;
  text-decoration:none;text-align:center;line-height:1.4}
.btn:hover{color:var(--ink);background:var(--face-2)}
.btn.primary{background:var(--sel);color:var(--sel-ink)}
.btn.primary:hover{color:var(--sel-ink)}
.field{display:block;margin-bottom:var(--s4)}
.field>label{display:block;font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;color:var(--ink-2);
  margin-bottom:var(--s2);letter-spacing:.8px}
.input{width:100%;background:var(--well);color:var(--ink);font-family:var(--f-body);font-size:16px;
  padding:12px 11px;line-height:1.6}
.input:focus{outline:1px dotted var(--ink);outline-offset:-5px}
.input::placeholder{color:var(--ink-3)}
textarea.input{resize:vertical;min-height:132px}

/* ═══ PIECES ═══ */
.row{display:flex;flex-wrap:wrap;gap:var(--s3);align-items:center}
/* min-width:0 for the same reason .win carries it: a grid item defaults to
   min-width:auto and will not shrink below its min-content. One column here
   holds a table.grid (min-width:340px), which was widening the whole column
   past the window on a 375px screen and scrolling the viewport sideways. */
.cols{display:grid;gap:var(--s5)}
.cols>*{min-width:0}

/* About is a single full-width column now that the spec sheet is gone. .prose
   caps at 64ch everywhere else, which would leave half the window empty here,
   so the measure is opened up — but not to full bleed: ~150 characters a line
   on a wide desktop is not readable, whatever the width allows. */
.about-text{max-width:88ch}
.about-text .prose{max-width:none}
/* Sibling .prose blocks get no separation by default — `.prose p+p` only
   matches paragraphs INSIDE a .prose, and `.doc .prose+.prose` is scoped to the
   journal entry. Measured on the live page: paragraphs 2-4 sat at margin-top 0,
   so About read as one wall of text. Same value the entry uses. */
.about-text .prose+.prose{margin-top:var(--s4)}
@media(min-width:820px){.c2{grid-template-columns:1fr 1fr;gap:var(--s6)}.c3{grid-template-columns:repeat(3,1fr)}}
.pill{font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;letter-spacing:0.2px;padding:9px 12px;background:var(--face);
  color:var(--ink-2)}
.pill.hl{color:var(--ink)}

/* hard-stop barber stripes — a pattern, not a gradient */
.construction{display:flex;align-items:center;padding:5px;
  background:repeating-linear-gradient(45deg,var(--blue) 0 10px,var(--bg) 10px 20px);
  background-size:28.3px 28.3px;animation:crawl 1.4s linear infinite}
.construction>span{flex:1;text-align:center;background:var(--face);color:var(--ink);
  font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;letter-spacing:0.5px;padding:11px 12px;line-height:1.6;
  box-shadow:inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo)}
@keyframes crawl{to{background-position:28.3px 0}}

/* PARKED: .skill and .meter have no markup right now — the System Properties
   window was removed on Emil's instruction, explicitly "for now". Kept so the
   section drops straight back in; restore the markup from commit 814e609.
   Do not delete these as dead CSS without checking that decision first. */
.skill{background:var(--face);padding:var(--s4)}
.skill .note{margin-top:var(--s2)}
.meter{background:var(--well);padding:3px;height:20px;margin-top:var(--s3)}
.meter>i{display:block;height:100%;
  background:repeating-linear-gradient(90deg,var(--blue) 0 7px,var(--well) 7px 10px)}

.tblwrap{overflow-x:auto}
table.grid{width:100%;border-collapse:collapse;font-size:14px;min-width:340px}
table.grid th{background:var(--face);font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;letter-spacing:0.4px;
  color:var(--ink-2);text-align:left;padding:10px 11px;font-weight:400;
  box-shadow:inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo)}
table.grid td{padding:12px 11px;border-bottom:1px solid var(--bev-lo2);vertical-align:top;line-height:1.65}
table.grid tr:hover td{background:var(--face-2)}
td.v{color:var(--ink)}
td.muted{color:var(--ink-3)}
.tag{font-family:var(--f-chrome);text-transform:uppercase;font-size:9px;padding:5px 7px;color:var(--ink-2);background:var(--face);
  display:inline-block;white-space:nowrap}
.tag.on{background:var(--sel);color:var(--sel-ink);box-shadow:none}

/* ═══ PHOTO EXPLORER ═══ */
.exp-bar{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;margin-bottom:var(--s2)}
.exp-bar .tb{font-size:10px;padding:0 11px;min-height:34px}
.crumb{flex:1;min-width:130px;background:var(--well);padding:10px 11px;
  font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.crumb b{color:var(--ink);font-weight:400}
.explorer{display:grid;grid-template-columns:186px 1fr;gap:var(--s2);align-items:start}
.tree{background:var(--well);padding:5px}
.tnode{display:flex;align-items:center;gap:9px;width:100%;background:none;border:0;color:var(--ink);
  font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;padding:11px 8px;cursor:pointer;text-align:left;line-height:1.5}
.tnode:hover{background:var(--face-2)}
.tnode.sel{background:var(--sel);color:var(--sel-ink)}
.tnode .fi{flex:none;color:var(--ink-2);font-size:13px}
.tnode.sel .fi{color:var(--sel-ink)}
.tnode .n{flex:1;overflow:hidden;text-overflow:ellipsis}
.tnode .ct{color:var(--ink-3);font-size:9px}
.tnode.sel .ct{color:var(--sel-ink)}
.tnode.kid{padding-left:20px}
.pane{background:var(--well);padding:var(--s3);min-height:240px}
.gal{display:grid;grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:var(--s3)}
.shot{background:var(--face);padding:4px;cursor:pointer;text-align:left;width:100%;display:block;
  box-shadow:inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo)}
.shot .img{display:block;height:112px;position:relative;overflow:hidden}
.shot .img::after{content:'';position:absolute;inset:0;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,.45) 0 1px,transparent 1px 3px)}
.shot .cap2{font-family:var(--f-chrome);text-transform:uppercase;font-size:9px;color:var(--ink-2);padding:8px 3px 3px;
  display:flex;justify-content:space-between;gap:8px;line-height:1.5}
.shot:hover .cap2{color:var(--ink)}
.gal-empty{font-family:var(--f-chrome);font-size:12px;color:var(--ink-3);padding:var(--s6) var(--s3);text-align:center}
/* Real photographs out of R2. The wells above are fixed-height boxes, so the
   image has to fill and crop rather than set its own size. `object-fit:contain`
   in the viewer because that is where you actually look at the photograph —
   cropping a portrait to a landscape well would hide half of it. */
.shot .img img{width:100%;height:100%;object-fit:cover;display:block}
.viewer .shotbig img{width:100%;height:100%;object-fit:contain;display:block;background:var(--well)}
.viewer .dlg{width:min(780px,100%)}
.viewer .shotbig{height:min(52vh,430px);position:relative;overflow:hidden}
.viewer .shotbig::after{content:'';position:absolute;inset:0;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,.45) 0 1px,transparent 1px 3px)}
.exif{display:grid;grid-template-columns:repeat(auto-fit,minmax(124px,1fr));gap:var(--s2);margin-top:var(--s3)}
.exif div{background:var(--face);padding:9px 10px;font-size:12px;color:var(--ink);line-height:1.6}
.exif span{display:block;font-family:var(--f-chrome);text-transform:uppercase;font-size:9px;color:var(--ink-3);margin-bottom:2px}
@media(max-width:760px){
  .explorer{grid-template-columns:1fr}
  .tree{display:flex;overflow-x:auto;padding:4px}
  .tnode{width:auto;flex:none;white-space:nowrap}
  .tnode.kid{padding-left:8px}
}

/* ═══ EXPERIENCE TIMELINE ═══
   A list with a spine. Square nodes, not dots — law 4 says no radius. The
   node on the current role is filled with the accent; every other one is
   window-face. Durations are computed in main.js from data-start/data-end,
   never written into the markup, so "Present" cannot go stale. */
.tl{list-style:none;position:relative;padding-left:26px}
.tl::before{content:'';position:absolute;left:6px;top:10px;bottom:10px;width:2px;
  background:var(--edge)}
.tl-item{position:relative;margin-bottom:var(--s3)}
.tl-item:last-child{margin-bottom:0}
.tl-node{position:absolute;left:-24px;top:17px;width:10px;height:10px;
  background:var(--face);
  box-shadow:inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo)}
.tl-item:first-child .tl-node{background:var(--blue)}

.tl-card{display:flex;gap:var(--s3);align-items:flex-start;
  background:var(--face);padding:var(--s3);
  box-shadow:
    inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo),
    inset 2px 2px 0 var(--bev-hi),inset -2px -2px 0 var(--bev-lo2)}
.tl-item:hover .tl-card{background:var(--face-2)}

/* Monogram instead of a logo: no image requests, no third-party marks, and
   it survives a theme flip. */
.tl-ico{width:38px;height:38px;flex:none;display:flex;align-items:center;
  justify-content:center;background:var(--well);color:var(--ink-2);
  font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;letter-spacing:0.2px}

.tl-main{flex:1;min-width:0}
.tl-role{font-family:var(--f-chrome);text-transform:uppercase;font-size:13px;line-height:1.65;color:var(--ink);
  font-weight:400;letter-spacing:.3px}
.tl-org{font-size:14px;color:var(--ink);line-height:1.7;margin-top:5px}
.tl-type{color:var(--ink-3)}
.tl-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);
  margin-top:7px;font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;color:var(--ink-2);
  line-height:1.8}
/* Separator lives on the computed value, so nothing dangles if JS never runs. */
.tl-dur{color:var(--ink)}
.tl-dur:not(:empty)::before{content:'·';margin-right:var(--s2);color:var(--ink-3)}
.tl-loc{font-size:12px;color:var(--ink-3);line-height:1.7;margin-top:5px}

@media(max-width:620px){
  .tl{padding-left:22px}
  .tl::before{left:5px}
  .tl-node{left:-20px}
  .tl-card{padding:var(--s2);gap:var(--s2)}
  .tl-ico{width:32px;height:32px;font-size:9px}
  .tl-role{font-size:12px}
}

/* Honeypot. Not display:none — some bots skip hidden fields, and a few
   password managers do too. Positioned off-canvas so it is unreachable by
   eye, pointer and tab, but still a real field a scraper will fill. */
.hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

/* ═══ SHARE CARD ═══
   A live preview of the Open Graph image: a Win95 window with the headline
   and a strip carrying the domain, which is the shape every social unfurl
   uses. Built from the same tokens as everything else, so it cannot drift
   from the site it is advertising. 1.91:1 is the aspect ratio Facebook,
   LinkedIn, X and Slack all crop to. */
.ogcard{aspect-ratio:1.91/1;display:flex;flex-direction:column;overflow:hidden;
  background:var(--face);padding:3px;margin-top:var(--s2);
  border:1px solid var(--edge);
  box-shadow:
    inset 1px 1px 0 var(--bev-hi2),inset -1px -1px 0 var(--bev-lo),
    inset 2px 2px 0 var(--bev-hi),inset -2px -2px 0 var(--bev-lo2)}
.ogcard-bar{background:var(--ttl);color:var(--ttl-ink);display:flex;align-items:center;
  gap:var(--s2);padding:5px 6px 5px 8px;font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;flex:none}
.ogcard-bar .dot{width:8px;height:8px;background:var(--ttl-ink);flex:none}
.ogcard-bar .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ogcard-bar .btns{display:flex;gap:2px}
.ogcard-bar .wb{width:16px;height:14px;background:var(--face);color:var(--ink);
  font-family:var(--f-chrome);text-transform:uppercase;font-size:8px;line-height:13px;text-align:center;
  border:1px solid var(--edge)}
.ogcard-body{flex:1;min-height:0;display:flex;flex-direction:column;justify-content:center;
  gap:var(--s2);padding:var(--s3) var(--s4);background:var(--bg);
  background-image:var(--pattern);background-size:180px 180px}
.ogcard-kicker{font-family:var(--f-chrome);text-transform:uppercase;font-size:9px;letter-spacing:0.5px;color:#FFFFFF;
  text-shadow:1px 1px 0 rgba(0,0,0,.6)}
.ogcard-title{font-family:var(--f-chrome);text-transform:uppercase;font-size:clamp(13px,2.4vw,17px);line-height:1.55;
  color:#FFFFFF;text-shadow:1px 1px 0 rgba(0,0,0,.6);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.ogcard-sum{font-family:var(--f-prose);font-size:12px;line-height:1.5;color:#FFFFFF;
  text-shadow:1px 1px 0 rgba(0,0,0,.6);opacity:.9;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ogcard-foot{flex:none;display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  background:var(--face);padding:7px 9px;font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;color:var(--ink-2);
  box-shadow:inset 0 1px 0 var(--bev-hi2)}
.ogcard-dom{color:var(--ink)}

/* ═══ JOURNAL ENTRY — the document view ═══
   Reading mode is a body class, not a separate page. It hides every other
   window and the icon rail, so the entry is the only thing on the desktop and
   the Memphis pattern survives as a thin margin rather than a background you
   are reading on top of. Routing, history and the URL bar are untouched —
   #entry is just another .win[data-path]. */
.entry{display:none}
body.reading .entry{display:block}
body.reading .main>.win:not(.entry){display:none}
body.reading .sidebar{display:none}
/* Maximised, like Notepad actually opens: the window runs nearly edge to edge
   and the Memphis pattern survives only as a thin frame. Line length is not at
   risk — .doc holds the text column to 68ch inside it regardless of how wide
   the window gets. */
body.reading .viewport{padding:var(--s3)}
body.reading .page{max-width:none}
@media(min-width:900px){
  body.reading .page{grid-template-columns:1fr}
}

/* Notepad's menu strip. Decorative — the real menus are in the browser chrome. */
.np-menu{display:flex;gap:var(--s4);background:var(--face);padding:7px 11px;
  font-family:var(--f-chrome);text-transform:uppercase;font-size:12px;color:var(--ink-2);user-select:none}
.np-menu u{text-decoration:underline}

.entry-body{padding:var(--s6) var(--s5)}
.doc{max-width:68ch;margin:0 auto}
.doc-head{margin-bottom:var(--s5)}
.doc-title{font-family:var(--f-ui);font-size:clamp(16px,2.4vw,24px);line-height:1.65;
  color:var(--ink);font-weight:400;letter-spacing:.4px;margin:var(--s3) 0 var(--s4)}
.doc-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);
  font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;color:var(--ink-2);line-height:1.9}
.doc-meta a{font-size:10px}

.prose.lead{font-size:20px;line-height:1.7}
.doc-h{font-family:var(--f-ui);font-size:16px;letter-spacing:.5px;color:var(--ink);
  font-weight:400;line-height:1.6;margin:var(--s6) 0 var(--s3)}
.doc .prose+.prose{margin-top:var(--s4)}

/* Figure — image or chart. The image well matches the gallery's placeholder
   language (flat fill + scanlines) so a real <img> can drop straight in. */
.fig{background:var(--face);padding:4px;margin:var(--s6) 0}
.fig .img{display:flex;align-items:center;justify-content:center;
  height:clamp(170px,30vw,300px);position:relative;overflow:hidden;background:var(--sel)}
.fig .img>img{width:100%;height:100%;object-fit:cover;display:block}
.fig .img::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,.45) 0 1px,transparent 1px 3px)}
.img-note{font-family:var(--f-chrome);font-size:10px;color:var(--sel-ink);opacity:.75;
  position:relative;z-index:1;padding:0 var(--s3);text-align:center;line-height:1.7}
.fig figcaption{font-family:var(--f-chrome);font-size:10px;color:var(--ink-2);
  padding:10px 5px 4px;line-height:1.85}
.fig figcaption em{color:var(--ink);font-style:italic}

/* Pull quote — Times, because it is prose, not chrome. */
.pull{background:var(--face-2);padding:var(--s5) var(--s5);margin:var(--s6) 0}
.pull p{font-family:var(--f-prose);font-size:19px;line-height:1.65;color:var(--ink)}

/* Boxed terminal. .term already sets the pixel face; this gives it a well. */
.term-block{background:var(--well);padding:var(--s4);margin:var(--s5) 0;
  font-family:var(--f-display);font-size:19px;line-height:1.6;color:var(--ink);
  overflow-x:auto;white-space:pre-wrap;word-break:break-word}
.term-block b{color:var(--blue);font-weight:400}
.term-block .c{color:var(--ink-3)}

/* Bar chart, built from the same parts as .meter — no library, themes free. */
.chart{display:grid;gap:var(--s2);padding:var(--s2) 0}
.chart-row{display:grid;grid-template-columns:96px 1fr 40px;align-items:center;gap:var(--s3)}
.chart-k{font-family:var(--f-chrome);text-transform:uppercase;font-size:9px;color:var(--ink-2);line-height:1.5}
.chart-bar{background:var(--well);padding:3px;height:22px}
.chart-bar>i{display:block;height:100%;min-width:2px;
  background:repeating-linear-gradient(90deg,var(--blue) 0 7px,var(--well) 7px 10px)}
.chart-v{font-family:var(--f-chrome);text-transform:uppercase;font-size:10px;color:var(--ink);text-align:right}

.linklist{list-style:none;display:grid;gap:var(--s3)}
.linklist li{line-height:1.7}
.doc-links{margin-top:var(--s5)}

.doc-nav{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);
  background:var(--face);padding:var(--s3)}
.doc-nav-sp{flex:1;min-width:0}

/* The one openable row in the journal reads as openable. */
tr.openable td:first-child a{font-weight:500}
tr.openable:hover td{background:var(--face-2)}

@media(max-width:620px){
  .entry-body{padding:var(--s4) var(--s3)}
  .prose.lead{font-size:18px}
  .pull{padding:var(--s4)}
  .pull p{font-size:17px}
  .term-block{font-size:17px;padding:var(--s3)}
  .chart-row{grid-template-columns:74px 1fr 34px;gap:var(--s2)}
  .doc-nav .btn{flex:1;min-width:132px}
  .doc-nav-sp{display:none}
}

/* ═══ 88×31 BADGES + COUNTER ═══ */
/* Scaled up from the historical 88x31 web-button size, which was unreadable
   once the pixel font left. The 2.84:1 proportion is preserved so they still
   read as period web badges rather than generic buttons — 124/44 = 2.82.
   The two-line text sets the floor: "NO WORDPRESS" at 12px Plex is ~86px, and
   the inner width here is ~116px, so it fits without wrapping oddly. */
.btn88{width:160px;height:56px;flex:none;display:flex;align-items:center;justify-content:center;
  text-align:center;font-family:var(--f-ui);text-transform:uppercase;font-size:16px;line-height:1.35;text-decoration:none;padding:4px;
  background:var(--face);color:var(--ink)}
.btn88:hover{background:var(--sel);color:var(--sel-ink)}
/* On a phone there is ~292px for these. Two-up gives each badge 138px, and
   "NO WORDPRESS" in 16px Silkscreen needs more than that — it wrapped to three
   lines while its neighbours stayed at two, so the grid came out ragged.
   Dropping the tracking bought 12px and still was not enough, and Silkscreen has
   no size between 8px and 16px to fall back to. So: one-up, full width. Every
   badge gets two clean lines and identical height. */
@media(max-width:560px){
  .btn88{width:100%;height:auto;min-height:54px;padding:8px 6px}
}
.btn88.inv{background:var(--sel);color:var(--sel-ink)}
.btn88.inv:hover{background:var(--face);color:var(--ink)}
.counter{display:inline-flex;gap:3px;background:var(--well);padding:4px}
.counter b{font-family:var(--f-display);font-size:21px;line-height:1;color:var(--ink);
  background:var(--bg);padding:3px 7px;font-weight:400}

/* ═══ MODAL DIALOG ═══ */
.modal{position:fixed;inset:0;z-index:200;display:none;align-items:center;justify-content:center;padding:var(--s4)}
.modal.open{display:flex}
.modal-back{position:absolute;inset:0;background:#000;opacity:.78}
html[data-theme="light"] .modal-back{background:#000;opacity:.4}
.dlg{position:relative;width:min(470px,100%);max-height:calc(100dvh - 40px);overflow:auto;
  background:var(--face);padding:3px;filter:drop-shadow(6px 6px 0 var(--drop))}
.dlg-body{display:flex;gap:var(--s4);padding:var(--s5) var(--s4)}
.dlg-icon{width:48px;height:48px;flex:none;display:flex;align-items:center;justify-content:center;
  font-size:24px;color:var(--ink);background:var(--well)}
.dlg-msg{font-size:14px;line-height:1.75}
.dlg-msg b{font-family:var(--f-chrome);text-transform:uppercase;font-size:13px;display:block;margin-bottom:var(--s3);
  color:var(--ink);font-weight:400;line-height:1.6}
.dlg-msg p+p{margin-top:.85em;color:var(--ink-2)}
.dlg-actions{display:flex;gap:var(--s2);justify-content:flex-end;flex-wrap:wrap;
  padding:var(--s3) var(--s4) var(--s4)}
.dlg-actions .btn{font-size:12px;padding:0 16px;min-height:40px}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
}

/* ═══ MOBILE ═══ */
@media(max-width:899px){
  body{font-size:15px}
  .mtrig{padding:12px 12px}
  .tb{min-height:42px}
  .exp-bar .tb{min-height:42px}
  .tnode{padding:13px 9px}

  /* The icon rail is NOT sticky here, deliberately. The menubar and toolbar
     live in .browser (position:fixed) and never scroll, and the Go menu lists
     every section — so pinning the icons as well bought nothing and cost a
     lot: ~120px of a phone screen permanently, a flat --bg band painted over
     the patterned desktop, and 6x70px of icons crammed into a ~347px strip
     that had to scroll sideways, hiding PHOTOS and MAIL.

     Instead the icons wrap as a real Win95 desktop icon grid, which is the
     same logic the desktop layout uses — icons on the pattern — just wrapped
     instead of stacked. Two balanced states only: one row of six, or two rows
     of three. Nothing overlays the scrolling content, so --nav-offset stays
     at its 20px default and main.js measures no rail. */
  .sidebar{display:grid;grid-template-columns:repeat(6,1fr);
    gap:var(--s3) var(--s1);margin:0;padding:var(--s1) 0 0}
  .dicon{width:auto;min-width:0;font-size:10px;padding:8px 2px}
  .dicon .glyph{width:44px;height:40px;font-size:19px;margin-bottom:8px}
  .dicon .cap{max-width:100%;overflow:hidden;text-overflow:ellipsis}
  .win-body{padding:var(--s4)}
  .win-body.tight{padding:var(--s3)}
  .viewport{padding:var(--s4) var(--s3) var(--s6)}
  .main{gap:var(--s4)}
  .prose{font-size:17px}
  .gal{grid-template-columns:repeat(2,1fr)}
  .dlg-body{padding:var(--s4) var(--s3)}
  .dlg-actions .btn{flex:1;min-width:120px}
}
/* Six across stops being comfortable once the columns drop under ~90px.
   Two rows of three keeps every icon balanced rather than leaving an orphan.
   If the System Properties window comes back this is 7 and 4 again. */
@media(max-width:560px){
  .sidebar{grid-template-columns:repeat(3,1fr);gap:var(--s4) var(--s2)}
}
@media(max-width:620px){
  .tb.opt{display:none}
  .toolbar{padding:5px;gap:5px}
  .urlwrap .loc{display:none}
  .menubar{overflow-x:auto}
  .mdrop{position:fixed;left:8px;right:8px;top:auto;min-width:0}
  .win-status>div:nth-child(2){display:none}
  .sbar>div:nth-child(3){display:none}
  .d1 .wordmark{width:min(100%,214px)}
}

/* ═══ PRINT ═══ */
@media print{
  :root{--bg:#fff;--face:#fff;--face-2:#fff;--well:#fff;--blue:#000;--ink:#000;--ink-2:#333;--ink-3:#666;
        --bev-hi:#fff;--bev-hi2:#fff;--bev-lo:#000;--bev-lo2:#999;--sel:#fff;--sel-ink:#000;
        --ttl:none;--ttl-ink:#000;--drop:transparent}
  body{background:#fff;color:#000}
  .raised,.sunken,.win,.btn,.tb,.skill,.dlg{box-shadow:none!important}
  .browser{position:static;height:auto;padding:0}
  .viewport{overflow:visible;padding:0}
  .menubar,.toolbar,.sbar,.tbar,.sidebar,.modal,.construction,.throbber,.win-title .btns{display:none!important}
  .win{border:1px solid #000;page-break-inside:avoid;margin-bottom:14px}
  .win-title{background:none;color:#000;border-bottom:1px solid #000}
  a{color:#000;text-decoration:underline}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:10px}
}
