/* huy.gg — ink
   ------------------------------------------------------------------
   Sumi is one pigment at several dilutions, which is why this is monochrome
   and warm at the same time. Ground is washi. Type runs from full ink to a
   pale wash. The only color on any page is the vermilion already inside the
   mark, so nothing is added and no accent is declared.

   Two structural ideas carry the layout. Ma (間): negative space is an active
   element, so the padding scale is generous and holds past the first screen
   rather than collapsing into a dense page. And tokonoma: each page opens on
   one thing, with the space around it doing the composing. */

:root{
  --washi:#EFE9DD;         /* ground. Same value the sphere plugin already uses. */
  --raise:#F5F1E8;         /* raised paper */
  --sumi:#161616;          /* full ink */
  --sumi1:#3E3A34;         /* two parts water */
  --sumi2:#565147;         /* four parts. Measured 6.52:1 on washi. It was
                              #6E6759 at 4.64, indistinguishable from sumi3's
                              4.56, so the file described four dilutions and
                              shipped three. Picked off the warm axis between
                              sumi1 and the old value, not sampled by eye. */
  --sumi3:#706858;         /* wash. Measured at 4.56:1 on washi; #9B9384 was
                              2.52:1 and carried the nav, every label, and every
                              caption on the site. Rules stay lighter: a hairline
                              is not text and is held to the 3:1 UI bound. */
  --rule:rgba(22,22,22,.14);
  --rule2:rgba(22,22,22,.3);
  --hair:rgba(22,22,22,.07);

  --face:Charter,"Bitstream Charter","Iowan Old Style","Palatino Linotype",Georgia,serif;
  --tech:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
  --pad:clamp(20px,5vw,64px);
}

*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}

body{background:var(--washi);color:var(--sumi1);font-family:var(--face);
     font-size:16px;line-height:1.66;-webkit-font-smoothing:antialiased;
     text-rendering:optimizeLegibility}

a{color:inherit;text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:4px;text-decoration-thickness:1px}
a:focus-visible{outline:2px solid var(--sumi);outline-offset:4px}
img{max-width:100%;height:auto;display:block}

.page{max-width:1280px;margin:0 auto}

/* ---------- the spine ----------
   A margin column runs the full height of every page carrying notation, with a
   continuous hairline between it and the content. It is the manuscript margin,
   and it is what makes the layout use its width instead of leaving a narrow
   column of text floating inside a wide frame. Content sits against the rule;
   the notation hangs outside it. */
.band,.alcove{display:grid;
  grid-template-columns:clamp(74px,8.5vw,132px) minmax(0,1fr);
  gap:0 clamp(16px,2.6vw,34px)}
/* The margin carries the stack an entry was built with, which is information a
   reader scans for, so it is not held at wash strength. It sits at sumi1, 9.34.
   When this changed, sumi2 was no step at all: 4.64 against sumi3's 4.56. That
   is what prompted fixing sumi2, and the middle rung now exists at 6.52 if this
   ever wants to come back down. Size is what keeps it subordinate: 12.5px mono,
   right-aligned, against a 25px serif title. */
/* A flex column pinned to the end edge, not plain right-aligned text: an item
   wider than the column, "computer vision", overflowed the right edge in normal
   flow and carried its bullet past the others. Pinned to flex-end the right
   edges hold and an over-long item grows left into empty margin. */
.mk{display:flex;flex-direction:column;align-items:flex-end;
    font-family:var(--tech);font-size:12.5px;line-height:1.65;color:var(--sumi1);
    text-align:right;padding-top:7px;position:sticky;top:16px;align-self:start}
/* The numeral leads its label at full ink, and sits on the title's line rather
   than floating above the margin: the 8px top padding is what drops its
   baseline onto the h2's. The gap under it separates the index from the stack,
   which are two different kinds of fact. It was an inline style repeated on
   every section, which is a rule with no home. */
/* The index is a different order of thing from the stack it heads, so it gets
   its own size rather than the label's. Not underlined: a rule here reads as a
   link on a page where .goto and .u are the only underlined things.

   It centers on the title's line box, not its baseline. Baseline alignment put
   the numeral visibly under the title's middle, because a 16px box against a
   30px line sits 7px low when both start at the same y. The 7px .mk padding is
   that correction. */
.mk .n{display:block;font-size:16px;line-height:1;color:var(--sumi);margin-bottom:12px}
/* One tool per line. Run together on wrapped lines the stack read as a phrase,
   and a reader looking for whether a thing uses Postgres had to parse a
   sentence. The right edges align against the rule, since that is the edge the
   margin hangs from; the bullets ride the ragged left. */
/* nowrap: "computer vision" and "power query" are single entries and broke
   across two lines in a 132px column, reading as two tools. Right alignment
   means an over-long item grows leftward into empty margin, never over content,
   and below 820px the whole spine collapses to a run-in line anyway. */
.mk .s{display:block;line-height:1.85;white-space:nowrap}
/* The bullet trails rather than leads. In a right-aligned column the last
   character of every line lands on the same x, so a trailing bullet draws a
   straight edge against the rule while a leading one rides the ragged side. */
.mk .s::after{content:"\2022";font-size:10px;margin-left:7px;color:var(--sumi3)}
/* Two labels in a 132px column, separated by the space between them and
   nothing else. A bullet had to earn its place here and could not: inline it
   landed mid-phrase, since both labels already wrap, and on its own line it was
   a glyph doing what a blank line does without adding ink to the margin. */
.mk .split{margin-top:15px}
.bd{border-left:1px solid var(--rule);padding-left:clamp(16px,2.6vw,34px);min-width:0}
.tech{font-family:var(--tech);font-size:10.5px;line-height:1.75;color:var(--sumi3);letter-spacing:0}
.tech a:hover{color:var(--sumi)}
.tech a[aria-current="page"]{color:var(--sumi)}

/* ---------- identity bar. Deliberately almost nothing. ---------- */
.bar{display:flex;align-items:center;gap:16px;padding:14px var(--pad);flex-wrap:wrap}
/* Mark and name are one link home, the convention a visitor already expects, so
   the way back does not depend on noticing the nav. On the home page itself
   they stay plain spans. */
.bar .home{display:inline-flex;align-items:center;gap:16px}
.bar .home:hover{text-decoration:none}
.bar .home:hover .who{text-decoration:underline;text-underline-offset:4px;
                      text-decoration-thickness:1px}
.bar .markbar{width:26px;height:26px;flex:0 0 auto;
              background:url("../img/logo.png") center/contain no-repeat}
/* The name holds its ratio to the nav: it was 15px against 10.5, so 1.43, and
   18 against 12.5 keeps it. The mark stays 26px, which is what sets the bar's
   height, so the row does not grow. */
/* line-height pinned to the mark's 26px: at 18px the body's 1.66 gives a 29.9px
   line box, which took over as the tallest thing in the row and grew the bar to
   58, breaking the footer it is matched to. */
.bar .who{font-size:17px;line-height:26px;color:var(--sumi)}
.bar .sp{flex:1}
.bar nav{display:flex;gap:20px;flex-wrap:wrap}
/* The nav carries a page's controls, so it outranks the margin notation rather
   than sitting a shade under it. At 10.5px in sumi2 it was the lighter of the
   two. Color does the work here, not font-weight: a mono face at 500 thickens
   into mud at this size. */
.bar nav a,.bar nav span{font-family:var(--tech);font-size:13px;font-weight:500;
                         color:var(--sumi1);letter-spacing:.01em}
.bar nav a:hover{color:var(--sumi)}

/* ---------- the alcove: one thing, and the space around it ---------- */
/* Trimmed from 11vw/8vw: the alcove was 502px tall and pushed the first
   figure 75px below the fold on a 1366x720 laptop. The air is still the
   point, it just no longer costs the evidence. */
.alcove{padding:clamp(40px,7vw,96px) var(--pad) clamp(34px,5vw,64px)}
.alcove .bd{display:grid;grid-template-columns:minmax(0,1fr) auto;
            gap:clamp(20px,3.4vw,48px);align-items:end}
.alcove h1{font-weight:400;font-size:clamp(36px,7.4vw,84px);line-height:1.02;
           letter-spacing:-.034em;color:var(--sumi);max-width:16ch;text-wrap:balance}
.alcove .railcard{justify-self:end}
.alcove h1{font-weight:400;font-size:clamp(34px,7vw,76px);line-height:1.03;
           letter-spacing:-.032em;color:var(--sumi);max-width:15ch;text-wrap:balance}

/* The inscription hangs in the margin, set vertically, which is the one place
   it reads as an inscription rather than as a stray label. Latin in vertical-rl
   rotates rather than stacks; held to one column, since a second reverses the
   reading order. */
.vert{writing-mode:vertical-rl;text-orientation:mixed;white-space:nowrap;
      font-family:var(--face);font-size:clamp(12.5px,1.3vw,14.5px);
      letter-spacing:.04em;color:var(--sumi2);margin-left:auto;padding-top:8px}

/* A rule-separated rail, framed. Not the hero metric card DESIGN.md bans:
   nothing tilts, nothing floats, and no single figure is set loose in a panel.
   It replaces the mark here, because the mark is identity and this space is
   worth evidence. On a 1366x720 laptop the first figure was 75px below the
   fold; putting it in the alcove is what recovers it. */
.railcard{flex:0 1 340px;min-width:250px;max-width:360px;border:1px solid var(--rule);
          background:var(--raise);padding:16px 18px 6px;margin-left:clamp(12px,3vw,40px);
          margin-top:6px}
/* 12px on sumi2, up from 10px on sumi3. It was the smallest text on any page,
   at wash strength, and it is the line that tells a reader what the four figures
   underneath it are. 4.56 to 6.52 on washi. */
.railcard .cap{font-family:var(--tech);font-size:12px;color:var(--sumi2);
               padding-bottom:11px;border-bottom:1px solid var(--rule)}
/* 9px rather than 11, matching the codemap rail. Four rows under a cap read as
   one table, and the looser step made them read as four separate lines. The
   last row drops its padding too, so the card's bottom edge sits on the text
   rather than on a phantom rule's worth of space. */
.railcard .row{display:flex;align-items:baseline;justify-content:space-between;
               gap:14px;padding:9px 0;border-bottom:1px solid var(--hair)}
.railcard .row:last-child{border-bottom:0;padding-bottom:0}
.railcard .k{font-size:13.5px;color:var(--sumi2);line-height:1.35}
.railcard .v{font-family:var(--tech);font-size:16px;color:var(--sumi);
             font-variant-numeric:tabular-nums;letter-spacing:-.02em;white-space:nowrap}

/* ---------- bands. Same breathing rate as the alcove, on purpose. ---------- */
.band{padding:clamp(30px,4.6vw,58px) var(--pad);border-top:1px solid var(--rule)}
/* A rule means a new item. A band that continues the entry above it carries no
   rule and closes the gap, so supporting detail reads as part of its project
   rather than as the next one. */
.band.cont{border-top:0;padding-top:0}
.bd > .k:first-child{margin-bottom:clamp(14px,2.4vw,26px)}

/* A lattice rather than a row of boxes: the gap is a rule, so the grid reads as
   ruled paper and fills the measure instead of leaving a ragged right edge. */
/* Two columns, fixed. auto-fit resolved to three at wide widths and left two
   dead cells showing the rule color through, because the lattice paints its
   gaps. An even column count divides the entries cleanly instead. */
.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
     gap:1px;background:var(--rule);border:1px solid var(--rule);margin-top:4px}
.two > div:last-child:nth-child(odd){grid-column:1/-1}
@media (max-width:640px){.two{grid-template-columns:1fr}}

/* Every project entry has one shape: prose at full width, then the visual at
   full width, then supporting detail in two columns underneath. A terminal is a
   visual and so is a confusion matrix, so neither gets squeezed into a side
   column while a screenshot elsewhere gets the whole width. */
.visual{margin:clamp(18px,2.6vw,28px) 0 0}
.detail{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
        gap:clamp(22px,3.2vw,44px);margin-top:clamp(16px,2.4vw,26px);align-items:start}
/* Supporting detail is one register. A bullet, a table row, and the paragraph
   beside them were setting at 14.5, 14.5, and 15.5px, which reads as three
   different treatments sitting in the same row. Figures stay mono and smaller
   on purpose: they are a different kind of thing, not a different size of the
   same thing. */
.detail p,.detail li,.detail td{font-size:14.5px;line-height:1.66}
.detail p{max-width:46ch}
.detail .met{font-size:12.5px}
@media (max-width:640px){.detail{grid-template-columns:1fr;gap:22px}}
/* The link sits at the foot of its cell, not wherever the prose happens to end,
   so two entries side by side put the same control on the same line. Left edge,
   because it aligns with the heading and the paragraph above it; a right-hung
   link would be the only element in the plate not on that axis. */
.two > div{background:var(--washi);padding:clamp(18px,2.4vw,28px);
     display:flex;flex-direction:column;align-items:flex-start}
/* `margin-top:auto` is what pushes the link down, but in the tallest cell it
   resolves to zero and the link ends up sitting against the last line of prose,
   reading as one more line of the paragraph. The floor lives on the paragraph
   instead, so the gap holds whether or not there is slack to distribute. */
.two > div > .u{margin-top:auto}
.two > div > p:last-of-type{margin-bottom:clamp(18px,2.6vw,26px)}
.two p{max-width:42ch}
/* Entry headings are set here and nowhere else. Inline sizes on some entries
   and none on others is what made three of them read as a different face. */
.bd h2{font-weight:700;font-size:clamp(21px,2.2vw,25px);line-height:1.2;
       letter-spacing:-.018em;color:var(--sumi);margin-bottom:11px}
.two h2{font-size:clamp(21px,2.6vw,28px);margin-bottom:10px}
p{color:var(--sumi1);max-width:46ch;font-size:15.5px}
p + p{margin-top:12px}
/* A project entry is one object: its opening paragraph and its outcome caption
   run the full width of the plate between them, rather than stopping short at a
   reading measure and leaving the entry looking like two narrow columns wrapped
   around a wide image. */
.wide p{max-width:none}
/* .u and .goto are one treatment. They were 11px and 13px, which meant the same
   action read as two different controls depending on where it sat. */

/* ---------- figures: ink at display size, never a card ---------- */
.figs{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));
      gap:clamp(20px,3.4vw,44px)}
.figs b{display:block;font-weight:400;font-size:clamp(32px,5vw,56px);line-height:1;
        letter-spacing:-.04em;color:var(--sumi);font-variant-numeric:tabular-nums}
.figs span{display:block;margin-top:11px;font-family:var(--tech);font-size:10.5px;
           color:var(--sumi3);line-height:1.7}

/* ---------- plates. The screenshots keep their color; it now means
              "real captured output" rather than decoration. ---------- */
/* A plate no longer navigates. It opens the capture at full size, which is what
   a reader clicking a screenshot is actually asking for, and it frees the entry
   to carry one honest link to the running thing instead. The cue is a corner
   badge rather than a cursor change alone, because a cursor is only discoverable
   once the pointer is already there. */
.plate{border:1px solid var(--rule);background:#fff;overflow:hidden;display:block;
       position:relative;cursor:zoom-in}
.plate::after{content:"enlarge";position:absolute;right:10px;bottom:10px;
              font-family:var(--tech);font-size:10px;letter-spacing:.06em;
              color:var(--sumi);background:rgba(245,241,232,.94);
              border:1px solid var(--rule2);padding:4px 9px;
              opacity:0;transition:opacity .18s ease;pointer-events:none}
.plate:hover::after,.plate:focus-visible::after{opacity:1}
@media (hover:none){.plate::after{opacity:.92}}
.platecap{font-family:var(--tech);font-size:10.5px;color:var(--sumi3);margin-top:11px;
          line-height:1.7;max-width:none}
.platecap b{color:var(--sumi);font-weight:400}

/* A code sample shown the way a plate shows an image: same rule, same white ground.
   It scrolls in its own box so a long dictionary never sets the page height. */
.codeplate{border:1px solid var(--rule);background:#fff;overflow:auto;
  max-height:clamp(300px,46vh,460px);padding:14px 16px;display:block}
.codeplate code{font-family:var(--tech);font-size:11.5px;line-height:1.66;
  color:var(--sumi1);white-space:pre;display:block}

/* The operational report, rendered from the same figures the dashboard reads rather than
   screenshotted, so it cannot drift out of agreement with them. Months run across, which is
   the source report's orientation, and that only fits because the money is abbreviated the
   way the dashboard KPIs already abbreviate it: at full precision a row label plus thirteen
   columns needs about 1,410px against an 800px measure. */
.reporttable{border:1px solid var(--rule);background:var(--raise);overflow:hidden}
table.rcmrep{width:100%;border-collapse:collapse}

/* One ink for every figure. Weighting two of the five rows put the emphasis where a reader
   had not asked for it; a report is a surface to read across, not an argument with a lead.
   The hovered row lifts to white instead, so emphasis follows the reader. */
table.rcmrep th[scope=row]{font-family:inherit;font-size:13.5px;font-weight:400;
  color:var(--sumi1);text-align:left;padding:11px 16px;white-space:nowrap}
table.rcmrep td{font-family:var(--tech);font-variant-numeric:tabular-nums;font-size:11.5px;
  color:var(--sumi1);text-align:right;white-space:nowrap;padding:11px 7px}
table.rcmrep thead th{font-family:var(--tech);font-size:11.5px;font-weight:400;color:var(--sumi2);
  letter-spacing:.06em;text-transform:uppercase;text-align:right;white-space:nowrap;
  padding:11px 7px;border-bottom:1px solid var(--rule)}
table.rcmrep thead th .yr{opacity:.58;margin-left:3px}
table.rcmrep .tot{padding-right:16px;border-left:1px solid var(--hair)}

/* Hover lights the whole row, label and total included, so the eye can hold a month while
   reading twelve columns of figures. */
table.rcmrep tbody tr:hover th,table.rcmrep tbody tr:hover td{background:#fff;color:var(--sumi)}
@media (max-width:720px){
  .reporttable{overflow-x:auto}
  table.rcmrep{min-width:660px}
}
/* No emphasis rule for lead paragraphs. Tried and removed on 2026-08-12: this
   design does emphasis by position and size, and it already runs four scanning
   layers, the margin notation, the titles, the railcard figures, and the
   platecap lead-ins, without bolding a word of running text. A single bolded
   phrase across six entries and four pages read as an accident rather than a
   system. If it ever comes back it goes on all six or none. */

/* Every entry that has a running destination says so on one line, sitting across
   from the entry's title and sharing its baseline, so the name of the thing and
   the way to reach it are read together rather than the route being buried in a
   supporting column further down. An address is spelled out only when it names a
   host other than this one, and then it is the object of the verb rather than a
   second field: "Visit carbon.huy.gg", not "Visit the landing page" trailed by a
   gray URL. huy.gg/dashboard would only repeat what the click already promises. */
.ehead{display:flex;align-items:baseline;justify-content:space-between;
       gap:clamp(18px,4vw,60px);flex-wrap:wrap}
/* The rule aligns with the title's baseline, not the link's text. Baseline
   alignment lines up the two sets of letters, but the link's rule then hangs
   3.3px lower, under its own descender and 3px of padding, and the eye reads
   the whole link as sitting low. Shifting by that measured amount puts the rule
   on the title's baseline; relative positioning, so the row's layout is
   untouched. */
.ehead .goto{margin-top:0;margin-bottom:11px;position:relative;top:-3.3px}
@media (max-width:640px){.ehead{gap:4px}.ehead .goto{margin-top:2px}}
/* 13px, matching the nav: at 11px a page's primary action was set smaller than
   its chrome. Not a filled button at rest. This palette has no filled shapes,
   so a solid block would outweigh the title beside it and five of them would
   stack into the page's dominant rhythm. The fill happens on hover instead, so
   the affordance is there when a pointer looks for it. */
.goto,.u{display:inline-flex;align-items:baseline;gap:12px;flex-wrap:wrap;
      margin-top:clamp(18px,2.6vw,26px);font-family:var(--tech);font-size:13px;
      color:var(--sumi);border-bottom:1px solid var(--rule2);padding:2px 5px 3px;
      margin-left:-5px;margin-right:-5px;
      transition:background .14s ease,color .14s ease}
.goto:hover,.u:hover{text-decoration:none;border-bottom-color:var(--sumi);
            background:var(--sumi);color:var(--washi)}

/* ---------- the lightbox ----------
   Ink at full strength over the page, so the capture is the only lit thing.
   Click the image to go between fit and native size, click the ground to leave. */
.lb{position:fixed;inset:0;z-index:90;display:none;
    background:rgba(22,22,22,.95)}
.lb.open{display:block}
.lb-stage{position:absolute;inset:0;overflow:auto;
          display:flex;align-items:center;justify-content:center;padding:clamp(16px,4vw,56px)}
.lb-stage img{max-width:100%;max-height:82vh;width:auto;height:auto;cursor:zoom-in;
              background:#fff;border:1px solid rgba(239,233,221,.25);margin:auto}
.lb-stage.in{display:block}
.lb-stage.in img{max-width:none;max-height:none;cursor:zoom-out;margin:0}
.lb-bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:baseline;
        gap:18px;padding:14px clamp(16px,4vw,56px);font-family:var(--tech);
        font-size:10.5px;line-height:1.6;color:rgba(239,233,221,.72);
        background:linear-gradient(rgba(22,22,22,0),rgba(22,22,22,.82) 42%)}
/* The caption runs the width of the bar. Capped at a reading measure it set to
   two fifths of the screen and wrapped three times under a very wide plate. */
.lb-txt{flex:1;max-width:none}
.lb-x{margin-left:auto;background:none;border:0;border-bottom:1px solid rgba(239,233,221,.45);
      padding:0 0 2px;font:inherit;color:var(--washi);cursor:pointer}
.lb-x:hover{border-bottom-color:var(--washi)}
.lb-x:focus-visible,.lb-stage img:focus-visible{outline:2px solid var(--washi);outline-offset:4px}

/* ---------- the ledger stays quiet ---------- */
table{width:100%;border-collapse:collapse;font-size:14.5px}
td,th{padding:11px 14px 11px 0;border-bottom:1px solid var(--hair);
      vertical-align:baseline;text-align:left;font-weight:400}
tr:last-child td{border-bottom:0}
.yr{font-family:var(--tech);font-size:11px;color:var(--sumi3);width:96px;white-space:nowrap}
.org{color:var(--sumi)}
.role{color:var(--sumi2)}
.met{text-align:right;font-family:var(--tech);font-size:12px;color:var(--sumi1);
     white-space:nowrap;font-variant-numeric:tabular-nums}

/* A bullet, not a middot: at 14.5px on washi the middot all but disappears, and
   a list marker that fades is a list without markers. It is the real list
   marker rather than an absolutely positioned ::before, because an abspos box
   anchors to the top of the item and rides above the text, while ::marker sits
   on the first line's baseline without being told to. */
ul.notes{list-style:disc outside;font-size:14.5px;line-height:1.66;
         color:var(--sumi1);max-width:62ch;padding-left:15px}
/* Resume bullets run wider than the 62ch book measure the prose uses. They sat
   at 450px inside an 805px cell, leaving 355px empty, and a one-to-three-line
   bullet does not need the tight measure that a paragraph does. 76ch is the top
   of the comfortable band rather than the full 111ch the cell would allow. */
.jobrow ul.notes{max-width:76ch}
ul.notes li{margin-bottom:7px}
/* Beside a paragraph, a list runs on the paragraph's rhythm. The 7px between
   items gave the left column 0.108 ink coverage against the right column's
   0.127 over the same area, and a block that is 18 percent less dense reads as
   a lighter font even though both measure the same: mean ink 111.7 against
   111.9 of 255, and an identical darkest pixel. Items here are single lines and
   do not need the separation; in .two, where they wrap, they keep it. */
.detail ul.notes li{margin-bottom:0}
ul.notes li::marker{color:var(--sumi3);font-size:11px}

.caps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:2px 34px}
@media (max-width:760px){.caps{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:460px){.caps{grid-template-columns:1fr}}
/* The rule belongs to the entry, not to every div inside it. `.caps div` also
   matched .d and .i, so each entry drew two hairlines: one under its own text
   and one at the stretched row bottom. A one-line entry sitting in a row with a
   wrapped one showed both, with the row's spare height between them. */
.caps > div{padding:9px 0;border-bottom:1px solid var(--hair)}
.caps .d{font-size:15px;color:var(--sumi)}
.caps .i{font-family:var(--tech);font-size:10.5px;color:var(--sumi3);margin-top:3px}

/* ---------- resume audience tabs ---------- */
/* The note is a caption for the tabs, so it sits against the rule rather than
   floating midway to the first job row, where it belonged to neither. */
.tabs{display:flex;gap:26px;flex-wrap:wrap;border-bottom:1px solid var(--rule2);
      padding-bottom:0;margin-bottom:9px}
.tabs button{background:none;border:0;padding:0 0 11px;cursor:pointer;font:inherit;
             font-size:15px;color:var(--sumi2);border-bottom:2px solid transparent;
             margin-bottom:-1px}
.tabs button:hover{color:var(--sumi)}
.tabs button[aria-selected="true"]{color:var(--sumi);border-bottom-color:var(--sumi)}
.tabs button:focus-visible{outline:2px solid var(--sumi);outline-offset:3px}
#r-note{font-family:var(--tech);font-size:12px;color:var(--sumi2);
        margin-bottom:clamp(20px,3vw,30px)}

.jobrow{display:grid;grid-template-columns:132px minmax(0,1fr);gap:0 24px;
        padding:20px 0;border-bottom:1px solid var(--hair)}
.jobrow.last{border-bottom:0}
.jobrow .when{font-family:var(--tech);font-size:11px;color:var(--sumi3);line-height:1.6;padding-top:5px}
.jobrow .when span{display:block;margin-top:3px}
.jobrow h3{font-weight:400;font-size:19px;letter-spacing:-.02em;color:var(--sumi);
           line-height:1.2;margin-bottom:2px}
.jobrow .org{font-size:13.5px;color:var(--sumi2);margin-bottom:10px}
/* Emphasis inside a bullet stays in the body face. Switching to mono made it
   read as a different kind of content rather than a more important piece of
   the same sentence, and two faces inside one paragraph is noise. Weight and
   full ink carry it instead: 600 against the 400 around it, #161616 against
   #3E3A34. Both element names are covered because the bullets are injected at
   runtime. */
.jobrow b,.jobrow strong{font-family:inherit;font-size:inherit;font-weight:600;
                         color:var(--sumi);letter-spacing:0}
/* Skill groups stand side by side rather than stacking into one long scroll.
   auto-fit rather than a fixed three: the operations tab has three groups, the
   other two have two, and a fixed count would leave an empty track. */
#r-skills{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
          gap:clamp(22px,3.4vw,48px)}
.skillgrp .k{margin-bottom:8px}

/* 16 above and 18 below a 20px row puts the footer at 54px, the header's exact
   height. It was 93px on a 16/56 split, taller than the header it was meant to
   match and lopsided within itself. */
/* Three zones on a 1fr auto 1fr grid, not a flex spacer. Availability is the
   line a hiring reader wants and it was glued to the copyright, reading as
   boilerplate. Flex would leave it wherever the two end groups happened to
   stop, and the left group is the wider of the two, so it would sit off center
   by about 45px and look like a mistake rather than a decision. */
footer{padding:16px var(--pad) 18px;border-top:1px solid var(--rule);
       display:grid;grid-template-columns:1fr auto 1fr;gap:20px;align-items:center}
footer .foot-l,footer .foot-r{display:flex;align-items:center;gap:20px}
footer .foot-r{justify-self:end}
footer .foot-c{justify-self:center;white-space:nowrap}
@media (max-width:640px){
  /* Below this the three zones collide, so they stack and read left. */
  footer{grid-template-columns:1fr;gap:10px}
  footer .foot-c,footer .foot-r{justify-self:start}
}
/* The footer matches the nav's size, 13px, and stays slim by holding its line
   box to the 20px mark rather than to 13 x 1.75. Height is unchanged at 54, the
   header's. Scoped to the footer so the .k.tech column labels inside entries
   keep their own size. */
footer.tech{font-size:13px;line-height:20px;color:var(--sumi2)}
footer .markfoot{width:20px;height:20px;opacity:.7;
                 background:url("../img/logo.png") center/contain no-repeat}
footer .sp{flex:1}

/* ---------- terminal motion ----------
   Both terminals write every line as opacity:0 with tfade forwards, and the
   cursor blinks on tblink. Neither keyframe came across when the scripts were
   pulled out of the old page into terminals.js, so the log feed and every typed
   response were still running and still invisible, and the cursor sat solid. */
@keyframes tfade{to{opacity:1}}
@keyframes tblink{to{opacity:0}}

/* Reduced motion keeps the fill: duration collapses to .001ms and the forwards
   fill still lands the line at opacity 1, so the output appears without the
   fade rather than disappearing with it. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms !important;transition-duration:.001ms !important}
}

@media (max-width:820px){
  /* The spine collapses: the margin column becomes a run-in line above the
     content, and the vertical inscription lies back down. Vertical text needs
     height, and below this there is none to give it. */
  .band,.alcove{grid-template-columns:1fr;gap:0}
  .mk{position:static;text-align:left;padding-top:0;margin-bottom:12px;
      display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}
  .mk span{display:inline !important}
  .bd{border-left:0;padding-left:0}
  .vert{writing-mode:horizontal-tb;margin-left:0;padding-top:0}
  .alcove .bd{grid-template-columns:1fr;gap:clamp(22px,5vw,34px);align-items:start}
  .alcove .vert{writing-mode:horizontal-tb;max-height:none;border-left:0;
                border-top:1px solid var(--rule);padding-left:0;padding-top:14px;
                margin-left:0;max-width:44ch}
  .yr{width:auto;display:block}
  td,th{padding:9px 10px 9px 0}
}
