/* Paper Dashboard: the site's stylesheet, ported from the Bento v8 artboard (design canvas, version 21).
   Tokens first, then the board and its faces, then the artboard's own rules as they are. */

/* Tokens: the light palette, then dark by system preference or by data-theme (round 2 sets it). */
:root {
  --accent: #2f5bea;
  --accent-text: #2f5bea;
  --bg: #f6f3ee;
  --tile: #ffffff;
  --ink: #16140f;
  --muted: #6f6a62;
  --line: rgba(20,18,16,0.08);
  --wash: #f2eee7;
  --shade: rgba(20,18,16,0.14);
  --shade2: rgba(20,18,16,0.3);
  --ok: #1d7a49;
  --ok-bg: rgba(34,160,94,0.1);
  --bd: #e3dacb;
  --accent-wash: rgba(47,91,234,0.35);
  --on-accent: #ffffff;
  --pt-bg: #ffffff;
  --menu-shadow: 0 18px 44px -24px rgba(20,18,16,0.35);
  --menu-bg: #ffffff;
  --term-accent: #8da5f3;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  --accent: #2f5bea;
  --accent-text: #8da5f3;
  --bg: #111114;
  --tile: #1a1a1f;
  --ink: #f3f1ec;
  --muted: #9c988f;
  --line: rgba(243,241,236,0.1);
  --wash: #24242a;
  --shade: rgba(243,241,236,0.18);
  --shade2: rgba(243,241,236,0.4);
  --ok: #4ade80;
  --ok-bg: rgba(74,222,128,0.12);
  --bd: #24242a;
  --accent-wash: rgba(47,91,234,0.35);
  --on-accent: #ffffff;
  --pt-bg: #232327;
  --menu-shadow: inset 0 1px 0 rgba(255,255,255,0.04), 0 0 0 1px rgba(243,241,236,0.06), 0 12px 32px rgba(0,0,0,0.55);
  --menu-bg: #24242a;
  --term-accent: #8da5f3;
  }
}
:root[data-theme="dark"] {
  --accent: #2f5bea;
  --accent-text: #8da5f3;
  --bg: #111114;
  --tile: #1a1a1f;
  --ink: #f3f1ec;
  --muted: #9c988f;
  --line: rgba(243,241,236,0.1);
  --wash: #24242a;
  --shade: rgba(243,241,236,0.18);
  --shade2: rgba(243,241,236,0.4);
  --ok: #4ade80;
  --ok-bg: rgba(74,222,128,0.12);
  --bd: #24242a;
  --accent-wash: rgba(47,91,234,0.35);
  --on-accent: #ffffff;
  --pt-bg: #232327;
  --menu-shadow: inset 0 1px 0 rgba(255,255,255,0.04), 0 0 0 1px rgba(243,241,236,0.06), 0 12px 32px rgba(0,0,0,0.55);
  --menu-bg: #24242a;
  --term-accent: #8da5f3;
}

/* The stage scales the 1440 x 900 board to fit the window, width and height, and centres it (his word, 2026-09-22); the phone is round 5.
   tan(atan2(a, b)) is a / b as a number in every browser; calc(100vw / 1440px) is typed arithmetic, which Firefox rejects. */
.stage { --scale: min(tan(atan2(100vw, 1440px)), tan(atan2(100vh, 900px))); width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; overflow: hidden; }
.board { position: relative; width: 1440px; height: 900px; box-sizing: border-box; overflow: hidden; padding: 32px; background: var(--bg, #f6f3ee); color: var(--ink, #16140f); display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); grid-template-rows: repeat(6, minmax(0, 1fr)); gap: 16px; flex: none; transform: scale(var(--scale)); transform-origin: center; }

/* The eight tiles: grid placement and inner layout, from the artboard's inline styles. A visitor's arrangement reaches
   them only as --<id>-column and --<id>-row on .board; each fallback is the designed placement, which
   Arrangement::DESIGNED repeats (test/models/arrangement_test.rb compares them). */
.tile-identity { grid-column: var(--identity-column, 1 / span 4); grid-row: var(--identity-row, 1 / span 2); display: flex; flex-direction: column; justify-content: space-between; padding: 24px; }
.tile-about { grid-column: var(--about-column, 5 / span 8); grid-row: var(--about-row, 1 / span 2); display: flex; flex-direction: column; justify-content: space-between; padding: 28px 32px; }
.tile-featured { grid-column: var(--featured-column, 1 / span 5); grid-row: var(--featured-row, 3 / span 3); display: flex; flex-direction: column; gap: 14px; padding: 24px; color: var(--ink, #16140f); }
.tile-more-work { grid-column: var(--more-work-column, 6 / span 3); grid-row: var(--more-work-row, 3 / span 3); display: flex; flex-direction: column; gap: 12px; padding: 20px; }
.tile-skills { grid-column: var(--skills-column, 9 / span 4); grid-row: var(--skills-row, 3 / span 2); display: flex; flex-direction: column; gap: 12px; padding: 20px 22px; }
.tile-experience { grid-column: var(--experience-column, 1 / span 4); grid-row: var(--experience-row, 6 / span 1); display: flex; flex-direction: column; justify-content: space-between; padding: 18px 22px; }
.tile-recruiters { grid-column: var(--recruiters-column, 5 / span 4); grid-row: var(--recruiters-row, 6 / span 1); display: flex; flex-direction: column; justify-content: space-between; padding: 14px 20px; }
.tile-dev-room { grid-column: var(--dev-room-column, 9 / span 4); grid-row: var(--dev-room-row, 5 / span 2); display: flex; flex-direction: column; align-items: center; padding: 0; }
/* Each tile carries both faces; only one shows. Round 2 toggles .open and .dim. */
.face { display: contents; }
.face-open { display: none; }
.tile.open .face-open { display: contents; }
.tile.open .face-compact { display: none; }
.tile.open { grid-column: 1 / -1; grid-row: 1 / -1; position: absolute; inset: -4px; z-index: 5; padding: 52px; cursor: default; animation: tile-in 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) backwards, expand 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
.tile.dim { opacity: 0.3; filter: blur(2px); pointer-events: none; }
/* The close button and the palette come after the tiles; the board shows them by clearing hidden. */
.overlay { display: contents; }
.overlay[hidden] { display: none; }
/* A page restored from Turbo's cache comes back at rest, without the entrance (the board marks itself before the cache). */
.board.played .tile { animation: none; }
/* The arrangement: on the desktop, with a mouse, the board stays full while a tile's border moves its cut or a carried
   tile docks or swaps (arrange_controller.js). Tiles have no z-index at rest; above them sit the outline where a carried
   tile would land (3), a gliding tile (2) and the lifted and the dropped one (4), then the open tile (5), the close
   button (6) and the palette (8); every tile isolates its own stacking, so nothing inside one (About's pill) rises past
   another. The lifted tile changes only its shadow: .tile transitions opacity and filter. Glides are Web Animations
   on translate, width, height and the dropped tile's scale, never transform (tile-in and expand use it); they beat
   normal declarations, so the open tile overrides them with !important. Reduced motion is checked in the controller. */
.arrange-drop { z-index: 3; box-sizing: border-box; border: 2px dashed var(--shade2, rgba(20, 18, 16, 0.3)); border-radius: 20px; pointer-events: none; }
.tile.gliding:not(.open, .lifted, .dropped) { z-index: 2; }
.tile.lifted:not(.open), .tile.dropped:not(.open) { z-index: 4; }
.tile.lifted { box-shadow: 0 30px 80px -30px rgba(20, 18, 16, 0.5); }
.tile.held { outline: 2px dashed var(--shade2, rgba(20, 18, 16, 0.3)); outline-offset: -6px; }
.tile.open { translate: none !important; scale: none !important; width: auto !important; height: auto !important; }
.board.arranging, .board.arranging * { -webkit-user-select: none; user-select: none; }
/* Reset's line, on an arranged board only: the state, so a visitor who comes back knows why the board differs, then
   Reset as a button in the board's pill style, so it reads as a control rather than a label. At 24 px the line sits
   centred in the 32 px margin, its right edge on the tiles' edge. The dot's halo is the accent at 18%, like the green
   dots' own halo, so it adds no colour. */
.arrange-bar { position: absolute; right: 32px; bottom: 4px; height: 24px; display: flex; align-items: center; gap: 14px; }
.arrange-state { display: inline-flex; align-items: center; gap: 9px; white-space: nowrap; }
.arrange-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: var(--accent, #2f5bea); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #2f5bea) 18%, transparent); }
.arrange-reset { height: 24px; display: inline-flex; align-items: center; gap: 6px; margin: 0; padding: 0 11px 0 9px; border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); border-radius: 999px; background: var(--tile, #ffffff); box-shadow: 0 1px 2px rgba(20, 18, 16, 0.06); font-family: "Space Grotesk", "Helvetica Neue", Arial, sans-serif; font-size: 12.5px; font-weight: 600; color: var(--ink, #16140f); cursor: pointer; }
.arrange-reset:hover { border-color: var(--shade2, rgba(20, 18, 16, 0.3)); }
.arrange-reset svg { display: block; flex: none; width: 13px; height: 13px; color: var(--accent-text, var(--accent, #2f5bea)); }
.board:has(> .tile.open) .arrange-bar { visibility: hidden; }
@media (min-width: 1024px) and (hover: hover) and (pointer: fine) {
  .board:is([data-edge="n"], [data-edge="s"]), .board:is([data-edge="n"], [data-edge="s"]) * { cursor: ns-resize; }
  .board:is([data-edge="e"], [data-edge="w"]), .board:is([data-edge="e"], [data-edge="w"]) * { cursor: ew-resize; }
  .board:is([data-edge="ne"], [data-edge="sw"]), .board:is([data-edge="ne"], [data-edge="sw"]) * { cursor: nesw-resize; }
  .board:is([data-edge="nw"], [data-edge="se"]), .board:is([data-edge="nw"], [data-edge="se"]) * { cursor: nwse-resize; }
  .board.arranging:not([data-edge]), .board.arranging:not([data-edge]) * { cursor: grabbing; }
  .board.arranging[data-blocked], .board.arranging[data-blocked] * { cursor: not-allowed; }
}

/* The artboard's rules. */
body { margin: 0; font-family: "Space Grotesk", "Helvetica Neue", Arial, sans-serif; -webkit-font-smoothing: antialiased; background: var(--bg); color: var(--ink); }
a { color: var(--accent-text, var(--accent, #2f5bea)); text-decoration: none; cursor: pointer; }
:focus-visible { outline: 2px solid var(--accent, #2f5bea); outline-offset: 2px; }
a:hover { color: var(--ink, #16140f); }
.mono { font-family: "JetBrains Mono", "Courier New", monospace; }
.tile { position: relative; isolation: isolate; cursor: zoom-in; transition: opacity 0.25s ease, filter 0.25s ease; background: var(--tile, #ffffff); border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); border-radius: 20px; box-shadow: 0 1px 2px rgba(20, 18, 16, 0.04), 0 12px 32px -20px rgba(20, 18, 16, 0.25); box-sizing: border-box; min-height: 0; min-width: 0; overflow: hidden; animation: tile-in 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.tile.open { cursor: default; }
.tile:nth-child(1) { animation-delay: 0.02s; } .tile:nth-child(2) { animation-delay: 0.08s; } .tile:nth-child(3) { animation-delay: 0.14s; } .tile:nth-child(4) { animation-delay: 0.2s; } .tile:nth-child(5) { animation-delay: 0.26s; } .tile:nth-child(6) { animation-delay: 0.32s; } .tile:nth-child(7) { animation-delay: 0.38s; } .tile:nth-child(8) { animation-delay: 0.44s; }
@keyframes expand { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } }
@keyframes tile-in { from { opacity: 0; transform: translateY(14px) scale(0.985); } to { opacity: 1; transform: none; } }
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes cell-in { from { opacity: 0; } to { opacity: 1; } }
.label { font-family: "JetBrains Mono", "Courier New", monospace; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted, #6f6a62); }
.tag { display: inline-flex; align-items: center; height: 26px; padding: 0 10px; border-radius: 999px; background: var(--wash, #f2eee7); color: var(--ink, #16140f); font-family: "JetBrains Mono", "Courier New", monospace; font-size: 11.5px; white-space: nowrap; }
.tag.ok { background: var(--ok-bg, rgba(34, 160, 94, 0.1)); color: var(--ok, #1d7a49); }
.btn { cursor: pointer; display: inline-flex; align-items: center; gap: 8px; height: 44px; padding: 0 18px; border-radius: 12px; font-weight: 600; font-size: 15px; white-space: nowrap; }
.btn-primary { background: var(--accent, #2f5bea); color: #ffffff; }
.btn-primary:hover { color: #ffffff; filter: brightness(0.92); }
.btn-ghost { border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); color: var(--ink, #16140f); background: var(--tile, #ffffff); }
.btn-ghost:hover { color: var(--accent-text, var(--accent, #2f5bea)); }
.btn-sm { cursor: pointer; display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 9px; font-weight: 600; font-size: 13px; white-space: nowrap; border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); color: var(--ink, #16140f); background: var(--tile, #ffffff); }
.btn-sm:hover { color: var(--accent-text, var(--accent, #2f5bea)); }
.btn-sm.fill { background: var(--ink, #16140f); color: var(--bg, #f6f3ee); border-color: var(--ink, #16140f); }
.btn-sm.fill:hover { color: var(--bg, #f6f3ee); filter: brightness(1.15); }
.node { width: 12px; height: 12px; border-radius: 50%; border: 2.5px solid var(--accent, #2f5bea); background: var(--tile, #ffffff); box-sizing: border-box; }
.card { cursor: pointer; border-radius: 14px; background: var(--wash, #f2eee7); }
.xp { display: flex; flex-direction: column; flex: 1; min-height: 0; width: 100%; box-sizing: border-box; }
.xhead { display: flex; align-items: center; justify-content: space-between; height: 44px; margin-right: 60px; flex: none; }
.bul { display: flex; gap: 12px; align-items: flex-start; font-size: 15px; line-height: 1.5; }
.bul::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--accent, #2f5bea); margin-top: 9px; }
.crow { display: flex; align-items: center; gap: 14px; height: 40px; border-top: 1px solid var(--line, rgba(20, 18, 16, 0.08)); font-size: 15px; }
.crow .k { width: 84px; flex: none; font-family: "JetBrains Mono", "Courier New", monospace; font-size: 12px; color: var(--muted, #6f6a62); letter-spacing: 0.06em; text-transform: uppercase; }
.fact { height: 230px; padding: 28px; border-radius: 16px; background: var(--wash, #f2eee7); display: flex; flex-direction: column; gap: 8px; box-sizing: border-box; }
.box { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; border-radius: 10px; background: var(--tile, #ffffff); border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); font-size: 13px; font-weight: 600; box-sizing: border-box; line-height: 1.2; }
.box .sub { font-family: "JetBrains Mono", "Courier New", monospace; font-size: 10.5px; font-weight: 400; color: var(--muted, #6f6a62); margin-top: 4px; }
.conn { width: 2px; height: 14px; background: var(--shade2, rgba(20, 18, 16, 0.3)); margin: 0 auto; }
.tool { display: flex; align-items: center; gap: 12px; height: 36px; font-size: 14px; font-weight: 600; }
.tool .n { margin-left: auto; font-weight: 400; color: var(--muted, #6f6a62); font-size: 13px; white-space: nowrap; }
.pill { position: absolute; top: 20px; right: 24px; z-index: 2; height: 30px; display: inline-flex; align-items: center; gap: 8px; padding: 0 12px 0 5px; border-radius: 999px; border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); background: var(--tile, #ffffff); font-size: 12.5px; color: var(--muted, #6f6a62); cursor: pointer; }
.pill:hover { color: var(--ink, #16140f); border-color: var(--shade2, rgba(20, 18, 16, 0.3)); }
.lcard { cursor: pointer; display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); background: var(--wash, #f2eee7); color: var(--ink, #16140f); min-width: 0; box-sizing: border-box; transition: border-color 0.15s ease; }
.lcard:hover { border-color: var(--accent-text, var(--accent, #2f5bea)); }
.lcard:hover .l2 { color: var(--accent-text, var(--accent, #2f5bea)); }
.lcard .lg { display: inline-flex; width: 22px; height: 22px; flex: none; align-items: center; justify-content: center; }
.lcard .lt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.lcard .l1 { font-family: "JetBrains Mono", "Courier New", monospace; font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted, #6f6a62); line-height: 1; }
.lcard .l2 { font-size: 14px; font-weight: 600; line-height: 1.1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lcard.wide { display: flex; width: 100%; height: 56px; padding: 0 16px; gap: 14px; }
.lcard.wide .lg { width: 24px; height: 24px; }
.lcard.wide .lg svg { width: 22px; height: 22px; }
.lcard.wide .l2 { font-size: 15px; }
.kbd { display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 22px; padding: 0 6px; border-radius: 6px; background: var(--wash, #f2eee7); border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); font-family: "JetBrains Mono", "Courier New", monospace; font-size: 11px; color: var(--muted, #6f6a62); box-sizing: border-box; white-space: nowrap; }
.prow { display: flex; align-items: center; gap: 12px; height: 40px; padding: 0 16px; cursor: pointer; font-size: 14px; font-weight: 500; color: var(--ink, #16140f); }
.prow:hover { background: var(--wash, #f2eee7); }
.prow .h { margin-left: auto; font-family: "JetBrains Mono", "Courier New", monospace; font-size: 11px; font-weight: 400; color: var(--muted, #6f6a62); white-space: nowrap; }
.prow .h.ok { color: var(--ok, #1d7a49); }
.psec { padding: 10px 16px 4px; font-family: "JetBrains Mono", "Courier New", monospace; font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted, #6f6a62); }
/* A résumé control without an uploaded file: dimmed, with no hover, and a click on it does nothing (nor opens its tile). */
.btn[aria-disabled="true"], .btn-sm[aria-disabled="true"], .prow[aria-disabled="true"] { cursor: default; opacity: 0.5; }
.btn-primary[aria-disabled="true"]:hover { filter: none; }
.btn-ghost[aria-disabled="true"]:hover, .btn-sm[aria-disabled="true"]:hover { color: var(--ink, #16140f); }
.prow[aria-disabled="true"]:hover { background: transparent; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.dot.on { background: #22a05e; box-shadow: 0 0 0 3px rgba(34, 160, 94, 0.18); }
.dot.off { background: var(--shade2, rgba(20, 18, 16, 0.3)); }
.seg { display: inline-flex; align-items: center; height: 30px; padding: 2px; box-sizing: border-box; border-radius: 999px; border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); background: var(--tile, #ffffff); gap: 2px; flex: none; }
.segc { display: inline-flex; align-items: center; height: 100%; padding: 0 8px; box-sizing: border-box; border-radius: 999px; font-family: "JetBrains Mono", "Courier New", monospace; font-size: 11.5px; letter-spacing: 0.06em; color: var(--muted, #6f6a62); cursor: pointer; user-select: none; }
.segc:hover { color: var(--ink, #16140f); }
.segc.on { background: var(--ink, #16140f); color: var(--tile, #ffffff); }
.cap .seg { height: 24px; }
.cap .segc { font-size: 10.5px; }

/* dev room */
@property --gx { syntax: '<number>'; inherits: false; initial-value: 14; }
.scene-box { width: 100%; height: 224px; flex: 1 0 auto; overflow: hidden; display: flex; justify-content: center; align-items: center; background: var(--box, #ece4d6); transition: background-color 0.25s ease; container: room / size; }
.tile.open .scene-box { height: 560px; flex: none; }
.scene { --px: 4px; position: relative; flex: none; width: calc(112 * var(--px)); height: calc(56 * var(--px)); overflow: hidden; isolation: isolate; contain: layout paint style; background: #ece4d6; }
.tile.open .scene { --px: 10px; }
.px { position: absolute; image-rendering: crisp-edges; image-rendering: pixelated; background-repeat: no-repeat; pointer-events: none; }
@keyframes strip { from { background-position-x: 0%; } to { background-position-x: 100%; } }
@keyframes screen-on { from { background-position-x: 50%; } to { background-position-x: 100%; } }
.sky { left: calc(45 * var(--px)); top: calc(6 * var(--px)); width: calc(18 * var(--px)); height: calc(14 * var(--px)); background: var(--sky, #8fc4f2); }
.room { left: 0; top: 0; width: calc(112 * var(--px)); height: calc(56 * var(--px)); background-image: url("/assets/room/room-88ad04f6.png"); background-size: 100% 100%; }
.screen { left: calc(18 * var(--px)); top: calc(15 * var(--px)); width: calc(12 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/screen-574e7e69.png"); background-size: 300% 100%; background-position-x: 50%; animation: screen-on 1.6s steps(2, jump-none) infinite; }
.plant { left: calc(102 * var(--px)); top: calc(18 * var(--px)); width: calc(10 * var(--px)); height: calc(14 * var(--px)); background-image: url("/assets/room/plant-5a777833.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 2.2s steps(2, jump-none) infinite; }
.cat { left: calc(75 * var(--px)); top: calc(32 * var(--px)); width: calc(14 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/cat-0f9985ba.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 1.3s steps(2, jump-none) infinite; }
.guy { left: calc(var(--gx, 14) * var(--px)); top: calc(20 * var(--px)); width: calc(16 * var(--px)); height: calc(24 * var(--px)); background-image: url("/assets/room/guy-303d1d6e.png"); background-size: 400% 700%; background-position-x: 0%; background-position-y: calc(100% * 2 / 6); animation: strip 0.6s steps(4, jump-none) infinite; transition: --gx var(--walk, 0ms) var(--tf, linear); }
.guy.flip { transform: scaleX(-1); }
.steam { left: calc(57 * var(--px)); top: calc(18 * var(--px)); width: calc(8 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/steam-0f38014f.png"); background-size: 300% 100%; background-position-x: 0%; animation: strip 0.9s steps(3, jump-none) infinite; visibility: hidden; }
.heart { left: calc(81 * var(--px)); top: calc(24 * var(--px)); width: calc(6 * var(--px)); height: calc(6 * var(--px)); background-image: url("/assets/room/heart-64205e06.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 0.7s steps(2, jump-none) infinite; visibility: hidden; }
.zzz { left: calc(26 * var(--px)); top: calc(12 * var(--px)); width: calc(8 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/zzz-3e62ec30.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 1.4s steps(2, jump-none) infinite; visibility: hidden; }
.night { inset: 0; background: rgb(28, 36, 96); opacity: var(--night, 0); mix-blend-mode: multiply; }
.glow { left: calc(13 * var(--px)); top: calc(10 * var(--px)); width: calc(28 * var(--px)); height: calc(26 * var(--px)); background: radial-gradient(closest-side, rgba(150, 200, 255, 0.55), rgba(150, 200, 255, 0)); opacity: var(--glow, 0); }
.scene[data-act="idle"] .guy { background-position-y: 0%; animation-duration: 1.2s; }
.scene[data-act="walk"] .guy { background-position-y: calc(100% * 1 / 6); animation-duration: 0.5s; }
.scene[data-act="type"] .guy { background-position-y: calc(100% * 2 / 6); animation-duration: 0.6s; }
.scene[data-act="coffee"] .guy { background-position-y: calc(100% * 3 / 6); animation-duration: 1s; }
.scene[data-act="pet"] .guy { background-position-y: calc(100% * 4 / 6); animation-duration: 0.9s; }
.scene[data-act="water"] .guy { background-position-y: calc(100% * 5 / 6); animation-duration: 1s; }
.scene[data-act="sleep"] .guy { background-position-y: 100%; animation-duration: 2.4s; }
.scene[data-act="coffee"] .steam { visibility: visible; }
.scene[data-act="pet"] .heart { visibility: visible; }
.scene[data-act="sleep"] .zzz { visibility: visible; }
.scene[data-act="sleep"] .screen { animation: none; background-position-x: 0%; }
.scene[data-act="sleep"] .glow { opacity: 0; }
.cap { width: 100%; height: 42px; flex: none; box-sizing: border-box; border-top: 1px solid var(--line, rgba(20, 18, 16, 0.08)); padding: 0 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.tile.open .cap { display: none; }
.cap-text:empty::before { content: "typing away"; }
.room-head { display: none; }
.tile.open .room-head { display: flex; align-self: stretch; margin-bottom: 24px; }
/* the two views (2D, 3D) are sibling scenes stacked in one box */
.stack { --px: 4px; position: relative; flex: none; width: calc(112 * var(--px)); height: calc(56 * var(--px)); }
.tile.open .stack { --px: 10px; }
.stack > .scene { transition: opacity 0.25s ease; }
/* An arranged, bigger dev room (round 8): the scene box takes the free height above the caption, and the art steps up in
   whole pixels as far as the box allows (112 x 56 art pixels). The designed, open and phone rooms keep their sizes. */
@media (min-width: 1024px) {
  @container room (min-width: 560px) and (min-height: 280px) { .tile:not(.open) :is(.stack, .scene) { --px: 5px; } }
  @container room (min-width: 672px) and (min-height: 336px) { .tile:not(.open) :is(.stack, .scene) { --px: 6px; } }
  @container room (min-width: 784px) and (min-height: 392px) { .tile:not(.open) :is(.stack, .scene) { --px: 7px; } }
  @container room (min-width: 896px) and (min-height: 448px) { .tile:not(.open) :is(.stack, .scene) { --px: 8px; } }
  @container room (min-width: 1008px) and (min-height: 504px) { .tile:not(.open) :is(.stack, .scene) { --px: 9px; } }
  @container room (min-width: 1120px) and (min-height: 560px) { .tile:not(.open) :is(.stack, .scene) { --px: 10px; } }
}
/* Compact faces (round 9, ADR-24): on the desktop every tile is a size container, and a tile smaller than its design
   shows the face its size chooses, at the same text sizes and limits: every span from Arrangement::MINIMUM up has one.
   Phone tiles take their content's height and have no container, so none of this reaches them. */
@media (min-width: 1024px) { .tile { container: tile / size; } }
/* Identity's compact faces (ADR-24). The content box is (116c - 66) x (142r - 66) px, so each step sits halfway between
   two spans: 340, 688 and 1036 px wide (3 | 4, 6 | 7 and 9 | 10 columns), 147 and 289 px tall (1 | 2 and 2 | 3 rows).
   Steps are written < and >=: min- and max- bounds both include their value, so a glide frame landing on a step would
   match two faces. 4+ x 2+ keeps the designed face. The rows carry inline display, gap and white-space, hence the !important. */
@media (min-width: 1024px) {
  /* A 3-column line holds 18 of the name's 25 characters: a name kept whole by a no-break space breaks rather than clips. */
  @container tile (width < 340px) { .tile-identity .cf-identity-name { overflow-wrap: anywhere; } }
  /* 3 x 1: the name alone, on up to 2 lines (60 of 76 px). */
  @container tile (width < 340px) and (height < 147px) {
    .tile-identity :is(.cf-identity-head, .cf-identity-links, .cf-identity-where) { display: none !important; }
    .tile-identity .cf-identity-role { display: none; }
    .tile-identity .cf-identity-who { margin-block: auto; }
  }
  /* 3 x 2: the name and the wrapped role over the cards (at most 173 of 218 px); the whole stack needs 269. */
  @container tile (width < 340px) and (147px <= height < 289px) {
    .tile-identity :is(.cf-identity-head, .cf-identity-where) { display: none !important; }
    .tile-identity .cf-identity-role { white-space: normal !important; }
  }
  /* 3 x 3+: the whole stack, city and clock on two lines when one can't hold both (472 px at their limits); a
     40-character status (312 px) never fits 282. */
  @container tile (width < 340px) and (height >= 289px) {
    .tile-identity .cf-identity-status { display: none !important; }
    .tile-identity .cf-identity-role { white-space: normal !important; }
    .tile-identity .cf-identity-where { flex-wrap: wrap; row-gap: 6px !important; }
  }
  /* 4-6 x 1: the name over the role, one line each as designed (55.5 of 76 px). */
  @container tile (340px <= width < 688px) and (height < 147px) {
    .tile-identity :is(.cf-identity-head, .cf-identity-links, .cf-identity-where) { display: none !important; }
    .tile-identity .cf-identity-who { margin-block: auto; }
  }
  /* 7-12 x 1: a query can't restyle the tile itself, so the face becomes the row; the name and role keep at least the
     designed 398 px beside two 145 px cards. */
  @container tile (width >= 688px) and (height < 147px) {
    .tile-identity .face-compact { display: flex; flex: 1; min-height: 0; align-items: center; gap: 24px; }
    .tile-identity .cf-identity-who { order: 1; flex: 1; }
    .tile-identity .cf-identity-links { order: 3; flex: none; width: 300px; }
    .tile-identity .cf-identity-where { display: none !important; }
  }
  /* The status (up to 312 px) joins the row from 10 columns, where the name still keeps 434 px. */
  @container tile (688px <= width < 1036px) and (height < 147px) { .tile-identity .cf-identity-head { display: none !important; } }
  @container tile (width >= 1036px) and (height < 147px) {
    .tile-identity .cf-identity-head { order: 2; flex: none; }
    .tile-identity .cf-identity-avatar { display: none !important; }
  }
}
/* About's compact faces (ADR-24). The content box is (116c - 82) x (142r - 74) px, so each step sits halfway between
   two spans: 208, 440, 556, 788 and 1020 px wide, 139 and 281 px tall, written < and >= so that no size, glide frames
   included, matches two faces. 8+ x 2+ keeps the designed face. Jump to… (the palette's only visible mouse entry) and
   the smile (the chat's) stay in every face, so the smile's pulse never replays. */
@media (min-width: 1024px) {
  /* 2-9 x 1: a launcher; with the band gone, the smile and Jump to… centre on the row (28 + (68 - 32) / 2 = 46). */
  @container tile (width < 1020px) and (height < 139px) {
    .tile-about .cf-about-band { display: none !important; }
    .tile-about :is(.cf-about-bubble, .cf-about-field) { display: none; }
    .tile-about .cf-about-ask { margin-block: auto; }
    .tile-about .cf-about-pill { top: 46px !important; }
  }
  /* 10-12 x 1: the field and the smile stop 143 px short, clear of the pill; the 90-character label (743 px) gets 821+. */
  @container tile (width >= 1020px) and (height < 139px) {
    .tile-about .cf-about-band { display: none !important; }
    .tile-about .cf-about-bubble { display: none; }
    .tile-about .cf-about-ask { margin-block: auto; margin-right: 143px; }
    .tile-about .cf-about-pill { top: 46px !important; }
  }
  /* 2 x 2+: TT and the presence dot (56 px) clear the kbd-less pill, the smile below. */
  @container tile (width < 208px) and (height >= 139px) {
    .tile-about .cf-about-band { margin-right: 88px !important; }
    .tile-about :is(.cf-about-presence, .cf-about-bubble, .cf-about-field) { display: none; }
  }
  /* 3-5 x 2: the band and the smile; a 230-character opening line needs the designed 520 px bubble or a third row. */
  @container tile (208px <= width < 556px) and (139px <= height < 281px) {
    .tile-about :is(.cf-about-bubble, .cf-about-field) { display: none; }
  }
  /* 6-7 x 2 and 3-7 x 3+: the band, the bubble and the smile; the field's 90 characters need 8 columns. */
  @container tile (556px <= width < 788px) and (139px <= height < 281px) { .tile-about .cf-about-field { display: none; } }
  @container tile (208px <= width < 788px) and (height >= 281px) { .tile-about .cf-about-field { display: none; } }
  /* Below 5 columns the band drops the name, as on the phone (Identity shows it): 25 characters need 206 px, and
     4 columns leave 118. */
  @container tile (width < 440px) { .tile-about .cf-about-name { display: none; } }
  /* At 2 columns Jump to… drops its kbd, as on the phone, to about 81 of the 150 px. */
  @container tile (width < 208px) {
    .tile-about .cf-about-pill { padding: 0 12px; }
    .tile-about .cf-about-kbd { display: none; }
  }
}
@media (min-width: 1024px) {
  /* Featured smaller than its 5 x 3 design (round 9), at the same text sizes and limits. Each threshold sits midway between
     two spans' content boxes, (116c - 66) x (142r - 66): 340, 456 and 572 wide, 147, 289 and 431 tall. It is <= on one
     side and > on the other, so every size a glide passes through falls on exactly one face. */
  /* under 5 columns a 64-character kicker would wrap beside its arrow, so the head row hides, arrow and all */
  @container tile (width <= 456px) { .tile-featured .cf-featured-head { display: none !important; } }
  /* the thumbnail needs 3 rows, and 4 at 3 columns, where the wrapped text fills 3 rows; there its overlay panel takes half
     its width so the two chips fit */
  @container tile (height <= 289px) or ((width <= 340px) and (height <= 431px)) { .tile-featured .cf-featured-thumb { display: none; } }
  @container tile (width <= 340px) { .tile-featured .cf-featured-panel { width: 50% !important; } }
  /* the summary needs 2 rows; 3 x 2 holds it or the tags, not both, and keeps the tags */
  @container tile (height <= 147px) or ((width <= 340px) and (height <= 289px)) { .tile-featured .cf-featured-summary { display: none; } }
  /* the tags need 2 rows, and 3 at 4-5 columns, where their two lines at the limit would not fit under the summary */
  @container tile (height <= 147px) or ((340px < width <= 572px) and (height <= 289px)) { .tile-featured .cf-featured-tags { display: none !important; } }
  /* alone in a 3-4 x 1 tile, the title (up to two lines) would otherwise hang at the top of its row */
  @container tile (width <= 456px) and (height <= 147px) { .tile-featured .cf-featured-text { margin-block: auto; } }
}
@media (min-width: 1024px) {
  /* More work around its 3 x 3 design (round 9): two cards at the same text sizes and limits. Each threshold sits midway
     between two spans' content boxes, (116c - 58) x (142r - 58): 232 and 580 wide, 155, 297 and 439 tall. It is <= on
     one side and > on the other, so every size a glide passes through falls on exactly one face. */
  /* 2 columns: a card's 142 px holds neither the arrow beside a title nor a row of tags (279 px at the limit) */
  @container tile (width <= 232px) { .tile-more-work :is(.cf-more-work-arrow, .cf-more-work-tags) { display: none !important; } }
  /* a summary needs its title and every line: a 2-row card (62 px) has no room for them up to 5 columns, and a 2-column
     card, at 19 characters a line, needs 4 rows */
  @container tile ((width <= 580px) and (height <= 297px)) or ((width <= 232px) and (height <= 439px)) { .tile-more-work .cf-more-work-summary { display: none; } }
  /* 6+ x 2: the cards side by side under the label, keeping everything; only here, and only closed, does the face leave
     display: contents */
  @container tile (width > 580px) and (155px < height <= 297px) {
    .tile-more-work:not(.open) .face-compact { display: grid; grid-template: auto minmax(0, 1fr) / repeat(2, minmax(0, 1fr)); gap: 12px; flex: 1; min-height: 0; }
    .tile-more-work .cf-more-work-label { grid-column: 1 / -1; }
  }
  /* 1 row: the label over the titles as plain lines, pinned to 20 px so two fit in 84 */
  @container tile (height <= 155px) {
    .tile-more-work .cf-more-work-card { padding: 0 !important; background: none; }
    .tile-more-work :is(.cf-more-work-summary, .cf-more-work-tags) { display: none !important; }
    .tile-more-work .cf-more-work-title { line-height: 20px; }
  }
  /* 2 x 1: a title may take two lines there, so only the first project shows */
  @container tile (width <= 232px) and (height <= 155px) { .tile-more-work .cf-more-work-card ~ .cf-more-work-card { display: none !important; } }
}
/* Skills' compact faces (round 9), by the tile's content box, (116c - 62) x (142r - 58) px: 170, 286 or 402+ wide and 84
   or 226+ tall. Each bound sits midway between two spans, and the conditions are ranges that meet there: a glide passes
   fractional sizes, which a max of 227.98px beside a min of 228px would leave between two faces. 4+ x 2+ is the designed
   terminal. The text keeps its 12 and 10.5px and every limit stays (name 13, used_in 39, evidence 57, argument 12).
   Hidden lines take display: none, which would replay their typing when a face shows them again, so arrange:begin marks
   the terminal played first (board#finishSkills). */
@media (min-width: 1024px) {
  /* 3 x 2+: beside a 16ch name a used_in gets 131.8px, 20 of its 39 characters, so the pointed row's used_in takes line 8
     (245.7 of 247px), where the evidence (359.1px) cannot. Static rows give it the body's frame, one line above the last
     for any number of skills, so the band sinks under their text; it takes no pointer, so line 8 lets the row go. */
  @container tile (228px <= width < 344px) and (height >= 155px) {
    .tile-skills :is(.skq-s, .sk3-ghost) { display: none; }
    .tile-skills .skq-row { position: static; }
    .tile-skills .skq-band { z-index: -1; }
    .tile-skills .skq-row .sk3-r { position: absolute; left: 15px; bottom: var(--ln); line-height: var(--ln); visibility: hidden; pointer-events: none; }
    .tile-skills .skq-row:hover .sk3-r { visibility: visible; }
  }
  /* 2 x 2+: the still terminal, names only. Line 1 fills 144.6 of 146px; a used_in would get 15.8px beside a name, the
     evidence 131px on line 8, a completed argument 146 of its 214px, the ghost 15.6px. */
  @container tile (width < 228px) and (height >= 155px) { .tile-skills :is(.skq-row .sk3-r, .skq-s, .skq-a, .sk3-ghost) { display: none; } }
  /* One row, 3+ columns: the names in 16ch cells like ls, without the command lines. The cells are 115.2px, 16ch at 12px
     (ch on the body would be 16px's); a name sheds its own 16ch, 12px wider than a cell's content. A cell's background
     stands in for the band; the names print at 1s, when line 1 would have started typing, and take the pointer at 1.45s. */
  @container tile (width >= 228px) and (height < 155px) {
    .tile-skills .cf-skills-term { --t2: 1s; }
    .tile-skills .skq-body { display: grid; padding-left: 9px; }
    .tile-skills :is(.cf-skills-list, .skq-band, .skq-l8, .sk3-l9, .skq-row .sk3-r) { display: none; }
    .tile-skills .skq-row { margin: 0; padding: 0 6px; animation-delay: 1s; }
    .tile-skills .sk3-n { width: auto; }
    .tile-skills .skq-row:hover { background: rgba(243, 241, 236, 0.1); border-radius: 4px; }
  }
  /* 4+ x 1: the label and 3 x 2 names (354.6 of 378px); 8px padding leaves the two lines 41.5px. */
  @container tile (width >= 344px) and (height < 155px) {
    .tile-skills .cf-skills-term { padding-block: 8px; }
    .tile-skills .skq-body { grid-template-columns: repeat(3, 115.2px); }
  }
  /* 3 x 1: the label would cost 26.5 of 84px and a line of names; without it the six fit 2 x 3 (239.4 x 60 of 262 x 64px).
     Its display is inline, hence !important. */
  @container tile (228px <= width < 344px) and (height < 155px) {
    .tile-skills .cf-skills-head { display: none !important; }
    .tile-skills .skq-body { grid-template-columns: repeat(2, 115.2px); }
  }
  /* 2 x 1: a launcher, the label over $ skills open alone (120.4 of 146px), typed at 1s as line 1 would be; a click
     opens the tile. */
  @container tile (width < 228px) and (height < 155px) {
    .tile-skills .cf-skills-term { --t2: 1s; }
    .tile-skills :is(.cf-skills-list, .skq-row, .skq-band, .skq-l8, .sk3-ghost) { display: none; }
  }
}
/* The dev room's compact faces (round 9), by the tile's content box, (116c - 18) x (142r - 18) px: 214, 330 or 446+ wide
   and 124 or 266+ tall. Each bound sits midway between two spans, and the conditions are ranges that meet there, so a
   gliding size never falls between two faces; 4+ x 2+ is the designed room. The room has no compact wrapper, so every
   rule skips the open tile. The art steps down by its box (the room container) as round 8 steps it up: the largest whole
   px whose 112 x 56 art fits, or crops at most 10px across. Its bounds are the tile's, since in flow the box is as wide
   as the tile and in one row as tall, so art and face change together in a glide. They stay in this block: the phone's
   box is a room container too, and these selectors would beat its 3px. */
@media (min-width: 1024px) {
  @container room (272px <= width < 388px) and (height >= 195px) { .tile:not(.open) :is(.stack, .scene) { --px: 3px; } }
  @container room (width < 272px) or (height < 195px) { .tile:not(.open) :is(.stack, .scene) { --px: 2px; } }
  /* 3 x 2+: caption and switch take 226.3 of 298px; with last push the cap would need 374.9 (421.1 at 18 characters).
     The 3px art (336px) crops 6px, all moved left: 2 art px of bare wall in 2D, of empty margin in 3D. */
  @container tile (272px <= width < 388px) and (height >= 195px) {
    .tile-dev-room:not(.open) .stack { translate: -3px 0; }
    .tile-dev-room:not(.open) .cf-dev-room-push { display: none; }
  }
  /* 2 columns, any height: the 2px art (224px) crops 10px; shifted 3px, that is 4 art px of bare wall on the left and 1
     on the right (the stand's corner in 2D, nothing in 3D). 3 x 1 and the strip crop nothing and stay centred. */
  @container tile (width < 272px) { .tile-dev-room:not(.open) .stack { translate: -3px 0; } }
  /* 2 x 2+: the caption alone (146.6 of 182px; with the switch, 226.3). V and the open room still switch the view. The
     span's display is inline, hence !important. */
  @container tile (width < 272px) and (height >= 195px) { .tile-dev-room:not(.open) .cf-dev-room-meta { display: none !important; } }
  /* 4+ x 1, a strip: no query reaches the tile's own column, so the box (224 x 124, the 2px art whole) and the cap sit
     side by side, absolutely. The cap stacks caption, last push (184.8 of 189px at 18 characters) and switch, 10px
     apart like the span holding the last two, whose inline align-items needs !important. */
  @container tile (width >= 388px) and (height < 195px) {
    .tile-dev-room:not(.open) .scene-box { position: absolute; inset: 0 auto 0 0; width: 224px; height: auto; }
    .tile-dev-room:not(.open) .cap { position: absolute; inset: 0 0 0 224px; width: auto; height: auto; flex-direction: column; align-items: flex-start; justify-content: center; gap: 10px; border-top: 0; border-left: 1px solid var(--line, rgba(20, 18, 16, 0.08)); }
    .tile-dev-room:not(.open) .cf-dev-room-meta { flex-direction: column; align-items: flex-start !important; }
  }
  /* 2-3 x 1: the art alone; the box takes the row, which its 224px basis would overflow. */
  @container tile (width < 388px) and (height < 195px) {
    .tile-dev-room:not(.open) .cap { display: none; }
    .tile-dev-room:not(.open) .scene-box { flex: 1 1 0; }
  }
}
@media (min-width: 1024px) {
  /* Experience below its 4 designed columns (round 9). Content box (116c - 62) x (142r - 54): 170 / 286 / 402 wide and
     88 / 230 tall, so the faces switch at the midpoints 228, 344 and 159. The points carry inline display, gap and
     margins, hence the !important. The partial renders the points oldest first and accents the last, so a face hides a
     point by the newer points after it and the newest keeps its accent. Every column below is wider than the designed
     128.7px, so a title the design holds on one line fits every face. */
  /* 3 x 1: the two newest points */
  @container tile (228px <= width < 344px) and (height < 159px) {
    .tile-experience .cf-experience-timeline { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
    .tile-experience .cf-experience-point:has(~ .cf-experience-point ~ .cf-experience-point) { display: none !important; }
  }
  /* 2 x 1: the newest point alone; its line still runs on to the edge, so the role reads as ongoing */
  @container tile (width < 228px) and (height < 159px) {
    .tile-experience .cf-experience-timeline { grid-template-columns: minmax(0, 1fr) !important; }
    .tile-experience .cf-experience-point:has(~ .cf-experience-point) { display: none !important; }
  }
  /* 2-3 x 2+: the row stands up, oldest at the top as it reads, the line down the nodes' centres and on past the newest.
     Each node spans the year and the role, so both share the text column. */
  @container tile (width < 344px) and (height >= 159px) {
    .tile-experience .cf-experience-timeline { grid-template-columns: minmax(0, 1fr) !important; }
    .tile-experience .cf-experience-line { inset: 7px auto 7px 5px !important; width: 2px; height: auto !important; }
    .tile-experience .cf-experience-point { display: grid !important; grid-template-columns: 12px minmax(0, 1fr); gap: 4px 10px !important; }
    .tile-experience .cf-experience-point > .node { grid-row: span 2; margin-top: 1px; } /* centred on the year's 14.5px line */
    .tile-experience .cf-experience-role { margin-top: 0 !important; } /* the 4px row gap replaces the pull-up */
  }
}
@media (min-width: 1024px) {
  /* For recruiters below its 4 designed columns (round 9). Content box (116c - 58) x (142r - 46): 174 / 290 / 406 wide and
     96 / 238 tall, so the faces switch at the midpoints 232, 348 and 167. Résumé and Email stay in every face. The head's
     align-items, the summary's white-space and the chips' display are inline, hence the !important. */
  /* Below 4 columns the buttons keep their words and drop their icons: any résumé or e-mail control on the page turns
     these labels into "Requested" and "Copied" for 1.6s, and both at once must still fit beside the label at 3 columns
     (280px of 290) and under it at 2 (172px of 174); with their icons they would not. */
  @container tile (width < 348px) { .tile-recruiters .cf-recruiters-head .btn-sm > svg { display: none; } }
  @container tile (width < 232px) { .tile-recruiters .cf-recruiters-head { flex-direction: column; align-items: flex-start !important; gap: 8px; } }
  /* 3 x 1: the whole summary on two lines, never cut by its ellipsis; the chips give way to it */
  @container tile (232px <= width < 348px) and (height < 167px) {
    .tile-recruiters .cf-recruiters-summary { white-space: normal !important; }
    .tile-recruiters .cf-recruiters-chips { display: none !important; }
  }
  /* 2 x 1: the first chip under the buttons; a 55-character summary needs three lines here, more than is left */
  @container tile (width < 232px) and (height < 167px) {
    .tile-recruiters .cf-recruiters-summary { display: none; }
    .tile-recruiters .cf-recruiters-chips > .tag ~ .tag { display: none; }
  }
  /* 2-3 x 2+: everything, wrapped, as on the phone */
  @container tile (width < 348px) and (height >= 167px) {
    .tile-recruiters .cf-recruiters-summary { white-space: normal !important; }
    .tile-recruiters .cf-recruiters-chips { flex-wrap: wrap; }
  }
}

/* 3D: the corner room, one baked pixel picture (iso_j.png) on the same 112 x 56 grid; back corner top at (56, 0), X0 = 56, Y0 = 24 */
/* every sprite is placed from the model (--x / --y in art px, computed in renderVals by proj()); the guy walks on --su / --sv, a u-leg then a v-leg */
@property --su { syntax: '<number>'; inherits: false; initial-value: 8; }
@property --sv { syntax: '<number>'; inherits: false; initial-value: 8; }
.scene.iso { position: absolute; left: 0; top: 0; background: var(--bd, #e3dacb); }
.iso .at { left: calc(var(--x, 0) * var(--px)); top: calc(var(--y, 0) * var(--px)); }
.sky3d { width: calc(var(--w, 11) * var(--px)); height: calc(var(--h, 14) * var(--px)); background: var(--sky, #8fc4f2); }
.room3d { left: 0; top: 0; width: calc(112 * var(--px)); height: calc(56 * var(--px)); background-image: url("/assets/room/iso_j-b00887f9.png"); background-size: 100% 100%; }
.screen3d { width: calc(12 * var(--px)); height: calc(13 * var(--px)); background-image: url("/assets/room/screen_iso-34167a37.png"); background-size: 300% 100%; background-position-x: 50%; animation: screen-on 1.6s steps(2, jump-none) infinite; }
.screen3d.flip { transform: scaleX(-1); }
.plant3d { width: calc(10 * var(--px)); height: calc(14 * var(--px)); background-image: url("/assets/room/plant-5a777833.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 2.2s steps(2, jump-none) infinite; }
.cat3d { width: calc(14 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/cat-0f9985ba.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 1.3s steps(2, jump-none) infinite; }
.guy3d { left: calc((56 + 2 * (var(--su) - var(--sv)) - 8) * var(--px)); top: calc((24 + var(--su) + var(--sv) - 24) * var(--px)); width: calc(16 * var(--px)); height: calc(24 * var(--px)); background-image: url("/assets/room/guy-303d1d6e.png"); background-size: 400% 700%; background-position-x: 0%; background-position-y: calc(100% * 2 / 6); animation: strip 0.6s steps(4, jump-none) infinite; transition: --su var(--w1, 0ms) steps(var(--n1, 1), end), --sv var(--w2, 0ms) steps(var(--n2, 1), end) var(--w1, 0ms); }
.guy3d.flip { transform: scaleX(-1); }
.steam3d { width: calc(8 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/steam-0f38014f.png"); background-size: 300% 100%; background-position-x: 0%; animation: strip 0.9s steps(3, jump-none) infinite; visibility: hidden; }
.heart3d { width: calc(6 * var(--px)); height: calc(6 * var(--px)); background-image: url("/assets/room/heart-64205e06.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 0.7s steps(2, jump-none) infinite; visibility: hidden; }
.zzz3d { width: calc(8 * var(--px)); height: calc(8 * var(--px)); background-image: url("/assets/room/zzz-3e62ec30.png"); background-size: 200% 100%; background-position-x: 0%; animation: strip 1.4s steps(2, jump-none) infinite; visibility: hidden; }
.glow3d { width: calc(28 * var(--px)); height: calc(26 * var(--px)); background: radial-gradient(closest-side, rgba(150, 200, 255, 0.55), rgba(150, 200, 255, 0)); opacity: var(--glow, 0); }
.scene[data-act="idle"] .guy3d { background-position-y: 0%; animation-duration: 1.2s; }
.scene[data-act="walk"] .guy3d { background-position-y: calc(100% * 1 / 6); animation-duration: 0.5s; }
.scene[data-act="type"] .guy3d { background-position-y: calc(100% * 2 / 6); animation-duration: 0.6s; }
.scene[data-act="coffee"] .guy3d { background-position-y: calc(100% * 3 / 6); animation-duration: 1s; }
.scene[data-act="pet"] .guy3d { background-position-y: calc(100% * 4 / 6); animation-duration: 0.9s; }
.scene[data-act="water"] .guy3d { background-position-y: calc(100% * 5 / 6); animation-duration: 1s; }
.scene[data-act="sleep"] .guy3d { background-position-y: 100%; animation-duration: 2.4s; }
.scene[data-act="coffee"] .steam3d { visibility: visible; }
.scene[data-act="pet"] .heart3d { visibility: visible; }
.scene[data-act="sleep"] .zzz3d { visibility: visible; }
.scene[data-act="sleep"] .screen3d { animation: none; background-position-x: 0%; }
.scene[data-act="sleep"] .glow3d { opacity: 0; }
.info { display: none; }
.tile.open .info { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 16px; width: 100%; height: auto; min-height: 64px; margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line, rgba(20, 18, 16, 0.08)); box-sizing: border-box; flex: none; }
.info .v { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 8px; }

/* Ask me: a chat window the recruiter drives */
@keyframes pop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: none; } }
@keyframes caption { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes menu-in { from { opacity: 0; transform: scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 var(--accent-wash, rgba(47, 91, 234, 0.35)); } 100% { box-shadow: 0 0 0 12px transparent; } }
@keyframes dot-bounce { 0%, 60%, 100% { transform: translateY(0); opacity: 0.5; } 30% { transform: translateY(-3px); opacity: 1; } }

.tt { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 32px; height: 32px; border-radius: 50%; box-sizing: border-box; background: var(--ink, #16140f); color: var(--tile, #ffffff); font-family: "JetBrains Mono", "Courier New", monospace; font-size: 12px; font-weight: 500; line-height: 1; letter-spacing: 0.02em; }
.thread { scrollbar-width: thin; scrollbar-color: var(--shade, rgba(20, 18, 16, 0.14)) transparent; }
.msg { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.ans { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; align-self: flex-start; max-width: 620px; min-width: 0; }
.q { align-self: flex-end; width: fit-content; max-width: 620px; box-sizing: border-box; background: var(--wash, #f2eee7); border-radius: 18px 18px 4px 18px; padding: 10px 16px; font-size: 16px; line-height: 1.35; font-weight: 500; color: var(--ink, #16140f); transform-origin: right bottom; animation: pop 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.hl { width: fit-content; max-width: 100%; box-sizing: border-box; background: var(--ink, #16140f); color: var(--tile, #ffffff); border-radius: 4px 18px 18px 18px; padding: 8px 18px; font-size: 22px; line-height: 1.2; font-weight: 600; letter-spacing: -0.01em; transform-origin: left top; animation: pop 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.pt { width: fit-content; max-width: 100%; box-sizing: border-box; background: var(--pt-bg, var(--tile, #ffffff)); border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); border-radius: 4px 18px 18px 18px; padding: 6px 12px; font-size: 15.5px; line-height: 1.35; color: var(--ink, #16140f); transform-origin: left top; animation: pop 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.pt.op { padding: 10px 16px; font-size: 16px; }
.ans > :nth-child(2) { animation-delay: 0.06s; }
.ans > :nth-child(3) { animation-delay: 0.12s; }
.ans > :nth-child(4) { animation-delay: 0.18s; }
.typing { align-self: flex-start; display: flex; align-items: center; gap: 4px; height: 34px; padding: 0 14px; box-sizing: border-box; background: var(--wash, #f2eee7); border-radius: 4px 18px 18px 18px; transform-origin: left top; animation: pop 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.tdot { width: 6px; height: 6px; border-radius: 50%; background: var(--muted, #6f6a62); animation: dot-bounce 0.9s ease-in-out infinite; }
.tdot:nth-child(2) { animation-delay: 0.15s; }
.tdot:nth-child(3) { animation-delay: 0.3s; }

.field { flex: 1; height: 40px; box-sizing: border-box; border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); border-radius: 999px; padding: 0 14px 0 18px; display: flex; align-items: center; gap: 12px; font-size: 15px; color: var(--muted, #6f6a62); cursor: pointer; user-select: none; transition: border-color 0.2s ease, background 0.2s ease; }
.field:hover, .field.open { border-color: var(--shade2, rgba(20, 18, 16, 0.3)); background: var(--wash, #f2eee7); }
.field svg { width: 16px; height: 16px; flex: none; display: block; margin-left: auto; color: var(--muted, #6f6a62); transition: transform 0.2s ease; }
.field.open svg { transform: rotate(180deg); }
.smile { flex: none; width: 40px; height: 40px; border-radius: 50%; border: 0; padding: 0; margin: 0; background: var(--accent, #2f5bea); color: var(--on-accent, #ffffff); display: grid; place-items: center; cursor: pointer; font: inherit; transition: transform 0.2s cubic-bezier(0.2, 0.7, 0.2, 1); }
.smile svg { width: 20px; height: 20px; display: block; }
.smile:hover { transform: scale(1.06); }
.smile.pulse { animation: pulse 1.8s ease-out 3; }

.backdrop { position: absolute; inset: 0; z-index: 2; background: transparent; }
.menu { position: absolute; right: 16px; bottom: 84px; width: 420px; box-sizing: border-box; background: var(--menu-bg, var(--tile, #ffffff)); border: 1px solid var(--line, rgba(20, 18, 16, 0.08)); border-radius: 16px; padding: 8px; box-shadow: var(--menu-shadow, 0 18px 44px -24px rgba(20, 18, 16, 0.35)); z-index: 3; transform-origin: right bottom; animation: menu-in 0.2s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.glab { display: block; padding: 10px 12px 4px; font-family: "JetBrains Mono", "Courier New", monospace; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted, #6f6a62); animation: caption 0.3s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.qrow { height: 44px; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 12px; box-sizing: border-box; border-radius: 10px; font-size: 15px; font-weight: 500; line-height: 1.2; color: var(--ink, #16140f); cursor: pointer; user-select: none; transition: background 0.15s ease; }
.qrow:hover { background: var(--wash, #f2eee7); }
.qrow svg { width: 16px; height: 16px; flex: none; display: block; color: var(--muted, #6f6a62); }

/* Skills v7 · the tile is a terminal picker (variant Q of the P / Q / R round, without the colour strip): a dark panel, nine lines of 20px, same in light and dark mode */
/* every keyframe has only a "from" and fill-mode backwards, so the base style IS the finished face (reduced motion, replays, static renders) */
/* ligatures off: "--" must stay two characters, or the typed reveal would slice a joined glyph */
.sk3 { --ln: 20px; --fs: 12px; --fs2: 10.5px; --fg: #d8d5cc; --val: #bdb9b0; --dim: #9c988f; --name: var(--term-accent, #8da5f3); --pr: #8fd460; --hot: var(--term-accent, #8da5f3); --key-bg: #33333a; --key-line: rgba(243, 241, 236, 0.16); --d1: 0.6s; --t2: 2.7s; flex: 1; min-height: 0; box-sizing: border-box; padding: 10px 12px; border-radius: 12px; background: #26262b; display: flex; flex-direction: column; justify-content: center; font-family: "JetBrains Mono", "Courier New", monospace; font-variant-ligatures: none; font-feature-settings: "calt" 0, "liga" 0; }
.sk3-ln { display: flex; align-items: center; height: var(--ln); flex: none; white-space: nowrap; }
.sk3-p { flex: none; width: 15px; font-size: var(--fs); font-weight: 500; color: var(--pr); }
/* --sk3-n = character count of the command, set on the span itself; --d1 = typing time of the first command, --t2 = when the second prompt lands */
.sk3-cmd { flex: none; display: inline-block; overflow: hidden; white-space: nowrap; font-size: var(--fs); color: var(--fg); width: calc(var(--sk3-n, 11) * 1ch); transition: color 0.15s ease; }
.sk3-cmd1 { animation: sk3-type var(--d1) steps(var(--sk3-n, 18), end) 1s backwards; }
.sk3-cmd2 { animation: sk3-type 0.4s steps(var(--sk3-n, 11), end) calc(var(--t2) + 0.05s) backwards; }
.sk3-n { flex: none; width: 16ch; font-size: var(--fs); font-weight: 500; color: var(--name); }
.sk3-r { font-size: var(--fs2); color: var(--val); }
.sk3-l9 .sk3-p { animation: sk3-in 0.01s linear var(--t2) backwards; }
.sk3-caret { flex: none; width: 0.6em; height: 1.2em; font-size: var(--fs); margin-left: 1px; background: var(--fg); transition: background-color 0.15s ease; animation: sk3-in 0.01s linear calc(var(--t2) + 0.05s) backwards, sk3-blink 1.2s step-end calc(var(--t2) + 0.45s) 8; }
.sk3-ghost { margin-left: 10px; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: var(--fs2); color: var(--dim); animation: sk3-in 0.3s ease calc(var(--t2) + 0.45s) backwards; }
.sk3-key { flex: none; margin-left: auto; width: 18px; height: 18px; box-sizing: border-box; border-radius: 5px; background: var(--key-bg); border: 1px solid var(--key-line); display: inline-flex; align-items: center; justify-content: center; color: var(--dim); transition: color 0.15s ease, border-color 0.15s ease; animation: sk3-in 0.3s ease calc(var(--t2) + 0.45s) backwards; }
.sk3-key svg { width: 12px; height: 12px; display: block; }
@keyframes sk3-type { from { width: 0; } }
@keyframes sk3-in { from { opacity: 0; } }
@keyframes sk3-blink { 50% { opacity: 0; } }
.sk3.played, .sk3.played *, .sk3.played *::before, .sk3.played *::after { animation: none !important; }
.tile.dim .sk3-caret { animation-play-state: running, paused; } /* hold the blink only; the caret still arrives on schedule */
@media (hover: hover) {
  .tile:hover .sk3-cmd2, .tile:hover .sk3-key { color: var(--hot); }
  .tile:hover .sk3-key { border-color: var(--hot); }
  .tile:hover .sk3-caret { background: var(--hot); }
}
.tile:focus-visible .sk3-cmd2, .tile:focus-visible .sk3-key { color: var(--hot); }
.tile:focus-visible .sk3-key { border-color: var(--hot); }
.tile:focus-visible .sk3-caret { background: var(--hot); }
@media (prefers-reduced-motion: reduce) { .sk3, .sk3 *, .sk3 *::before, .sk3 *::after { animation: none !important; transition: none !important; } }
/* the list prints in one frame, a selection band climbs to the first row, then follows the pointer (CSS only, sibling selectors: rows come before the band, line 8 and line 9) */
/* line 8 is empty at rest, the blank line a terminal leaves before the next prompt; while a row is pointed at it shows that skill's sentence from the open view */
/* hover swaps use visibility / max-width, never display: display would restart the backwards-filled entrance of the swapped element */
.skq { --t2: 2.65s; }
/* the rows do not answer the pointer until the entrance is over: a row that is still invisible must not take a click, and a pointer resting on the list must not bend the climb or push the caret */
.skq-body { position: relative; isolation: isolate; flex: none; animation: skq-hold 0.01s linear calc(var(--t2) + 0.45s) backwards; }
/* the row is as wide as the band (6px past the text on both sides), so the pointer never sits on the band without a row under it */
.skq-row { position: relative; z-index: 1; margin: 0 -6px; padding: 0 6px 0 21px; cursor: pointer; animation: sk3-in 0.01s linear 1.75s backwards; }
.skq-row .sk3-r { transition: color 0.15s ease; }
/* the band must not take the pointer, or the row under it loses :hover and the band flickers */
.skq-band { position: absolute; z-index: 0; top: var(--ln); left: -6px; right: -6px; height: var(--ln); border-radius: 4px; background: rgba(243, 241, 236, 0.1); pointer-events: none; display: flex; align-items: center; animation: sk3-in 0.01s linear 1.95s backwards, skq-climb 0.6s steps(5, end) 1.95s backwards; }
.skq-gt { flex: none; width: 15px; margin-left: 6px; font-size: var(--fs); font-weight: 500; color: var(--dim); }
.skq-l8 { position: relative; }
.skq-s { position: absolute; left: 15px; top: 0; height: var(--ln); line-height: var(--ln); font-size: var(--fs2); color: var(--val); white-space: nowrap; visibility: hidden; }
.skq-a { flex: none; display: inline-block; max-width: 0; margin-left: 0; overflow: hidden; white-space: nowrap; font-size: var(--fs); color: var(--hot); animation: sk3-in 0.01s linear calc(var(--t2) + 0.45s) backwards; }
@keyframes skq-climb { from { transform: translateY(calc(5 * var(--ln))); } }
@keyframes skq-hold { from { pointer-events: none; } }
@media (hover: hover) {
  .skq-r2:hover ~ .skq-band { transform: translateY(var(--ln)); }
  .skq-r3:hover ~ .skq-band { transform: translateY(calc(2 * var(--ln))); }
  .skq-r4:hover ~ .skq-band { transform: translateY(calc(3 * var(--ln))); }
  .skq-r5:hover ~ .skq-band { transform: translateY(calc(4 * var(--ln))); }
  .skq-r6:hover ~ .skq-band { transform: translateY(calc(5 * var(--ln))); }
  .skq-row:hover ~ .skq-band .skq-gt { color: var(--hot); }
  .skq-row:hover .sk3-r { color: var(--fg); }
  .skq-r1:hover ~ .skq-l8 .skq-s1, .skq-r2:hover ~ .skq-l8 .skq-s2, .skq-r3:hover ~ .skq-l8 .skq-s3, .skq-r4:hover ~ .skq-l8 .skq-s4, .skq-r5:hover ~ .skq-l8 .skq-s5, .skq-r6:hover ~ .skq-l8 .skq-s6 { visibility: visible; }
  .skq-r1:hover ~ .sk3-l9 .skq-a1, .skq-r2:hover ~ .sk3-l9 .skq-a2, .skq-r3:hover ~ .sk3-l9 .skq-a3, .skq-r4:hover ~ .sk3-l9 .skq-a4, .skq-r5:hover ~ .sk3-l9 .skq-a5, .skq-r6:hover ~ .sk3-l9 .skq-a6 { max-width: 12ch; margin-left: 1ch; }
}
/* open view: the skill whose row was clicked */
.skrow.skmark { box-shadow: inset 2px 0 0 var(--accent-text, var(--accent, #2f5bea)); padding-left: 16px; }
.skrow.skmark .ev { color: var(--ink, #16140f) !important; }

@media (prefers-reduced-motion: reduce) { .scene .px { animation-play-state: paused; } .guy { transition: none; } .tile { animation: none !important; } .bar, .cell, .cellL { animation: none; } .q, .hl, .pt, .typing, .tdot, .menu, .glab, .smile.pulse { animation: none !important; } .field, .field svg, .smile, .qrow { transition: none !important; } .stack > .scene, .guy3d, .scene-box { transition: none !important; } }

/* Touch and the phone (round 5, 2026-09-22): two blocks ported from the artboard BentoV8Phone; everything above stays as it was. */
@media (hover: none) {
  /* Touch (round 5): a tap must not leave a hover state behind, and the terminal's rows are not tap targets (a tap opens the tile). */
  a:hover { color: var(--accent-text, var(--accent, #2f5bea)); }
  .btn-primary:hover, .btn-sm.fill:hover { filter: none; }
  .btn-ghost:hover, .btn-sm:hover { color: var(--ink, #16140f); }
  .pill:hover { color: var(--muted, #6f6a62); border-color: var(--line, rgba(20, 18, 16, 0.08)); }
  .lcard:hover { border-color: var(--line, rgba(20, 18, 16, 0.08)); }
  .lcard:hover .l2 { color: inherit; }
  .prow:hover, .qrow:hover { background: transparent; }
  .segc:hover:not(.on) { color: var(--muted, #6f6a62); }
  .field:hover:not(.open) { border-color: var(--line, rgba(20, 18, 16, 0.08)); background: transparent; }
  .smile:hover { transform: none; }
  .skq-row { pointer-events: none; }
  .skq-s, .skq-a { display: none; }
  .tile, [role="button"] { -webkit-tap-highlight-color: transparent; }
  .arrange-reset:hover { border-color: var(--line, rgba(20, 18, 16, 0.08)); }
  /* Skills' compact faces (round 9): a tap leaves neither line 8's used_in nor a cell's background behind (the rows take
     no pointer here anyway). */
  @container tile (228px <= width < 344px) and (height >= 155px) { .tile-skills .skq-row:hover .sk3-r { visibility: hidden; } }
  @container tile (width >= 228px) and (height < 155px) { .tile-skills .skq-row:hover { background: transparent; } }
}

@media (max-width: 1023.98px) {
  /* The phone (round 5): the board stacks in one column and scrolls; an open face is a full-screen sheet against the
     stage, which locks to one screen while a sheet or the palette is open; the palette is a bottom sheet. */
  .stage { position: relative; display: block; width: auto; height: auto; min-height: 100vh; overflow: visible; }
  .stage:has(.tile.open), .stage:has(.overlay:not([hidden]) .pal) { height: 100vh; height: var(--screen, 100dvh); overflow: hidden; }
  .board { display: grid; position: static; transform: none; width: auto; height: auto; max-width: 600px; margin: 0 auto; overflow: visible; padding: 16px; grid-template-columns: minmax(0, 1fr); grid-template-rows: none; gap: 12px; }
  .tile { grid-column: auto !important; grid-row: auto !important; animation: none; }
  .board > .tile-identity { order: 1; gap: 16px; animation: tile-in 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; animation-delay: 0.02s; }
  .board > .tile-about { order: 2; gap: 14px; padding: 28px 20px 20px; animation: tile-in 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; animation-delay: 0.08s; }
  .board > .tile-dev-room { order: 3; animation: tile-in 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; animation-delay: 0.14s; }
  .board > .tile-recruiters { order: 4; gap: 10px; }
  .board > .tile-featured { order: 5; }
  .board > .tile-more-work { order: 6; }
  .board > .tile-skills { order: 7; }
  .board > .tile-experience { order: 8; gap: 10px; }
  .tile.dim { filter: none !important; }
  /* nothing is arranged on the phone: the stack ignores the tiles' cells, and Reset's line and the outline never show */
  .arrange-bar, .arrange-drop { display: none; }

  /* the compact faces */
  .tile-identity .face-compact > div:nth-child(2) > div:nth-child(2) { white-space: normal !important; }
  .tile-identity .face-compact > div:last-child { flex-wrap: wrap; row-gap: 6px; }
  .tile-about .face-compact > div:nth-child(2) { margin-right: 100px !important; }
  .tile-about .face-compact > div:nth-child(2) > span:nth-child(2) { display: none; }
  .tile-about .face-compact .pill { right: 20px; padding: 0 12px; }
  .tile-about .face-compact .pill .kbd { display: none; }
  .tile-dev-room .scene-box { height: 168px; }
  .tile-dev-room .scene, .tile-dev-room .stack, .tile-dev-room.open .scene, .tile-dev-room.open .stack { --px: 3px; }
  .tile-dev-room .cap > span:last-child > .mono { display: none; }
  .tile-recruiters .face-compact > div:nth-child(2) { white-space: normal !important; }
  .tile-recruiters .face-compact > div:nth-child(3) { flex-wrap: wrap; }
  .tile-featured .thumb { min-height: 160px !important; }
  .tile-skills .sk3 { --fs: 11px; --fs2: 9.5px; }
  .tile-skills .sk3-n { width: 14ch; }
  .tile-skills .sk3-r { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

  /* the open faces as sheets */
  .tile.open { position: absolute !important; inset: 0 !important; z-index: 5 !important; padding: 12px 16px 24px !important; border: 0 !important; border-radius: 0 !important; box-shadow: none !important; overflow-y: auto !important; overscroll-behavior: contain; }
  .tile.open .xp { flex: none; width: 100%; max-width: 600px; margin: 0 auto; }
  .tile-about.open .xp { flex: 1; min-height: 0; }
  .tile.open .xhead { height: auto; min-height: 44px; margin-right: 48px; flex-wrap: wrap; row-gap: 4px; padding: 6px 0; }
  .tile.open .xhead:not(.room-head) > .mono { flex-basis: 100%; min-width: 0; margin-left: 0; white-space: normal; }
  .xclose { top: 12px !important; right: 12px !important; width: 40px !important; height: 40px !important; }
  .xgrid { grid-template-columns: minmax(0, 1fr) !important; gap: 28px !important; }

  .tile-identity.open .xgrid > div:first-child > div:first-child { width: 56px !important; height: 56px !important; font-size: 20px !important; }
  .tile-identity .xtitle { font-size: 34px !important; }
  .tile-identity.open .xgrid > div:first-child > div:nth-child(4) { font-size: 16px !important; }
  .tile-identity.open .xgrid > div:first-child > p { font-size: 15px !important; }
  .crow { height: auto; min-height: 40px; padding: 8px 0; align-items: flex-start; font-size: 14px; }

  .tile-about.open .q { font-size: 15px; }
  .tile-about.open .pt { font-size: 15px; }
  .tile-about.open .hl { font-size: 19px; padding: 8px 16px; }
  .tile-about.open .menu { left: 0; right: 0; width: auto; bottom: 84px; max-height: calc(100% - 100px); overflow-y: auto; }

  .tile-featured .xtitle { font-size: 34px !important; }
  .tile-featured.open .xgrid > div:first-child > p { font-size: 15px !important; }
  .tile-featured.open .bul { font-size: 14.5px; }
  .tile-featured.open .xgrid > div:first-child > div:last-child { flex-direction: column; align-items: stretch !important; margin-top: 24px !important; }
  .tile-featured.open .xgrid > div:first-child > div:last-child .btn { justify-content: center; }
  .diagram { height: auto !important; flex-direction: column !important; padding: 16px !important; gap: 16px !important; }
  .diagram > div:first-child > div { padding: 12px !important; }
  .diagram .box { max-width: 100%; box-sizing: border-box; }
  .diagram svg[width="404"] { width: 100%; height: auto; }
  .dia-link, .dia-bracket { display: none !important; }
  .dia-side { width: auto !important; flex-direction: row !important; flex-wrap: wrap; gap: 12px; }
  .dia-side > .mono { flex-basis: 100%; }
  .dia-side .box { flex: 1; margin-top: 8px !important; }

  .xrow { flex: none !important; grid-template-columns: minmax(0, 1fr) !important; gap: 20px !important; padding: 20px !important; }
  .tile-more-work .xtitle { font-size: 24px !important; }
  .tile-more-work.open .xrow p { font-size: 15px !important; }
  .tile-more-work.open .bul { font-size: 14.5px !important; }

  .skrow { max-height: none !important; flex: none !important; padding: 12px 0 !important; }
  .skrow > div:first-child > span:first-child { font-size: 17px !important; }
  .skfoot { height: auto !important; flex-wrap: wrap; padding: 12px 0; gap: 10px !important; }
  .tool { height: auto; min-height: 36px; flex-wrap: wrap; row-gap: 2px; }
  .tool .n { flex-basis: 100%; margin-left: 0; padding-left: 30px; white-space: normal; }

  .xentry { grid-template-columns: 24px minmax(0, 1fr) !important; }
  .xentry > :nth-child(1) { grid-column: 2; grid-row: 1; padding-top: 0 !important; }
  .xentry > :nth-child(2) { grid-column: 1; grid-row: 1 / span 2; }
  .xentry > :nth-child(3) { grid-column: 2; grid-row: 2; }
  .xentry > div:last-child > span:first-child { font-size: 17px !important; }
  .tile-experience .xentry .xtitle { font-size: 20px !important; }
  .tile-experience.open .bul { font-size: 14.5px !important; }
  .tile-experience.open .xgrid > div:last-child > div:first-child { padding: 20px !important; }

  .facts { grid-template-columns: minmax(0, 1fr) !important; gap: 12px !important; }
  .fact { height: auto; padding: 20px; }
  .fact > span:nth-child(2) { font-size: 22px !important; }
  .facts + div { flex-wrap: wrap; row-gap: 10px; }

  .tile-dev-room.open .scene-box { height: 168px !important; width: calc(100% + 32px); margin: 0 -16px; }
  .tile-dev-room.open .info { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 16px; }
  .tile-dev-room.open .info > :last-child { grid-column: 1 / -1; }
  .tile-dev-room.open .room-head > span > .mono { display: none; }

  /* the palette as a bottom sheet; on a short screen (a phone on its side) it scrolls instead of squeezing its rows */
  .pal { left: 0 !important; right: 0 !important; top: auto !important; bottom: 0 !important; width: auto !important; border-radius: 18px 18px 0 0 !important; max-height: calc(100% - 24px); overflow-y: auto !important; }
  .pal > * { flex: none; }
  .pal .kbd { display: none; }
  .pal .prow { height: 44px; }
  .pfoot { display: none !important; }
}
