/* ————————————————————————————————————————————————————————————
   eustin.co — "The Cabinet"

   The shelf is deliberately quiet and almost colorless. All of the
   color on this page belongs to the apps: each panel carries its own
   palette, its own display face, and its own voice, so the collection
   reads as four committed worlds standing next to each other rather
   than one theme wearing four hats. Letter's panel is newsprint-white
   on purpose — the app is a riso zine, and the shelf should say so.

   Every panel therefore scopes its own --bg / --fg / --fg-muted /
   --edge / --accent / --face, and the shared .panel rules only ever
   read those variables. Adding a fifth app = one new palette block.
   ———————————————————————————————————————————————————————————— */

:root {
  /* the shelf itself — warm near-black, so it sits under the apps
     rather than competing with any of them */
  --room:        #141110;
  --room-edge:   #2a2320;
  --room-fg:     #ede6dd;
  --room-muted:  #9b9088;

  --face-ui:   "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --face-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* 11px is the floor everywhere, matching the house rule in Eats */
  --fs-micro: 0.6875rem;
  --fs-sm:    0.8125rem;
  --fs-base:  0.9375rem;

  --radius: 16px;
  --ease:   cubic-bezier(0.2, 0.7, 0.3, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--room);
  color: var(--room-fg);
  font-family: var(--face-ui);
  font-size: var(--fs-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* A single warm pool of light behind the shelf — the one gesture the
   neutral room allows itself, so the panels feel lit rather than pasted. */
.room {
  min-height: 100dvh;
  padding: clamp(48px, 9vw, 104px) clamp(20px, 5vw, 32px) 56px;
  background:
    radial-gradient(120% 70% at 50% -10%, #241d18 0%, transparent 62%);
}

.skip {
  position: absolute;
  left: -9999px;
  padding: 10px 16px;
  border-radius: 8px;
  background: var(--room-fg);
  color: var(--room);
  font-size: var(--fs-sm);
  font-weight: 600;
}
.skip:focus { left: 16px; top: 16px; z-index: 10; }

/* ——— the masthead ——————————————————————————————————————— */

.micro {
  margin: 0;
  font-family: var(--face-mono);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--room-muted);
}

.masthead {
  max-width: 860px;
  margin: 0 auto clamp(36px, 6vw, 60px);
  text-align: center;
}

.wordmark {
  margin: 14px 0 0;
  font-family: "Young Serif", Georgia, serif;
  font-size: clamp(2.75rem, 11vw, 4.5rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
}
.wordmark .dot { color: #6f625a; }

.lede {
  max-width: 40ch;
  margin: 20px auto 0;
  color: var(--room-muted);
  text-wrap: balance;
}

/* the one quiet door that isn't an app: the builder's own page */
.masthead-links { margin: 18px 0 0; }
.footer-bio { margin: 10px 0 0; }

.bio-link {
  font-family: var(--face-mono);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--room-muted);
  border-bottom: 1px solid var(--room-edge);
  padding-bottom: 3px;
  transition: color 220ms var(--ease), border-color 220ms var(--ease);
}
.bio-link:is(:hover, :focus-visible) {
  color: var(--room-fg);
  border-color: var(--room-fg);
}

/* ——— the shelf ————————————————————————————————————————— */

.shelf {
  display: grid;
  gap: clamp(14px, 2.2vw, 20px);
  max-width: 860px;
  margin: 0 auto;
}
@media (min-width: 720px) {
  .shelf { grid-template-columns: 1fr 1fr; }
}

/* ——— a panel ——————————————————————————————————————————— */

.panel {
  --lift: 0px;

  display: flex;
  flex-direction: column;
  min-height: 300px;
  padding: 22px 22px 20px;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--fg);
  text-decoration: none;
  overflow: hidden;
  position: relative;
  isolation: isolate;
  transform: translateY(var(--lift));
  transition:
    transform 220ms var(--ease),
    border-color 220ms var(--ease),
    box-shadow 220ms var(--ease);
}

/* the app's own light, pooled in the top-left of its panel */
.panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(85% 60% at 12% 0%, var(--accent) 0%, transparent 70%);
  opacity: 0.13;
  transition: opacity 220ms var(--ease);
}

.panel:is(:hover, :focus-visible) {
  --lift: -4px;
  border-color: var(--accent);
  box-shadow: 0 18px 40px rgb(0 0 0 / 0.45);
}
.panel:is(:hover, :focus-visible)::before { opacity: 0.26; }

.panel:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.panel-micro {
  margin: 0;
  font-family: var(--face-mono);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
}

.mark {
  display: block;
  margin: 20px 0 16px;
}
.mark svg { display: block; width: 46px; height: 46px; }

.panel-name {
  margin: 0;
  font-family: var(--face);
  font-size: clamp(1.5rem, 4.4vw, 1.875rem);
  font-weight: var(--face-weight, 400);
  line-height: 1.06;
  letter-spacing: var(--face-tracking, -0.01em);
  text-transform: var(--face-case, none);
}

/* shorthand samples inside a description stay in the app's mono voice */
.panel-desc code {
  font-family: var(--face-mono);
  font-size: 0.92em;
  color: var(--accent-ink, var(--accent));
}

.panel-desc {
  margin: 12px 0 0;
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--fg-muted);
  text-wrap: pretty;
  /* the floor on the gap below; the go-line's auto margin adds the rest */
  margin-bottom: 22px;
}

/* Pinned to the bottom edge so all four baselines line up across the shelf.
   The auto top margin is what does the pinning — descriptions run two or
   three lines depending on the app, and without it the slack collects
   under the go-line instead of above it, stepping the footers out of
   line with each other. */
.panel-go {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: auto 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--edge);
  font-family: var(--face-mono);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
}
.panel-go .host { margin-right: auto; }

/* The two trailing controls — the arrow on a real tile, the "soon" chip on
   the slot — share one height, so the rule above the go-line lands on the
   same pixel whichever one a panel is wearing. */
.arrow, .soon {
  display: inline-flex;
  align-items: center;
  height: 22px;
}

.arrow {
  font-family: var(--face-ui);
  font-size: var(--fs-sm);
  color: var(--accent-ink, var(--accent));
  transition: transform 220ms var(--ease);
}
.panel:is(:hover, :focus-visible) .arrow { transform: translateX(4px); }

/* ——— the brewing slot ——————————————————————————————————— */

/* Coffee isn't deployed yet, so it is a slot in the cabinet, not a
   link. It keeps the panel's shape (the shelf stays a 2×2) but drops
   every affordance that would promise somewhere to go: no lift, no
   glow, and a dashed edge that reads as a reserved space rather than
   a door. That also keeps it from muddling into the Eats panel, which
   is the other warm-brown tile on the shelf. */
.is-brewing {
  cursor: default;
  border-style: dashed;
}
.is-brewing::before { opacity: 0.05; }
/* The shared hover rules outrank the plain .is-brewing selectors, so the
   slot has to turn them off by name — otherwise it lifts, glows, and
   lights its border exactly like the three tiles that go somewhere,
   which is the one promise this tile must not make. */
.is-brewing:is(:hover, :focus-visible) {
  --lift: 0px;
  border-color: var(--edge);
  box-shadow: none;
}
.is-brewing:is(:hover, :focus-visible)::before { opacity: 0.05; }
.is-brewing .mark,
.is-brewing .panel-name { opacity: 0.9; }

.soon {
  padding: 3px 8px;
  border: 1px solid var(--edge);
  border-radius: 6px;
  font-size: var(--fs-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
}

.dots { display: inline-flex; gap: 3px; margin-left: 7px; vertical-align: 1px; }
.dots i {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  animation: bubble 1.6s var(--ease) infinite;
}
.dots i:nth-child(2) { animation-delay: 0.2s; }
.dots i:nth-child(3) { animation-delay: 0.4s; }

@keyframes bubble {
  0%, 65%, 100% { opacity: 0.25; transform: translateY(0); }
  32%           { opacity: 1;    transform: translateY(-2px); }
}

/* ——— the palettes ——————————————————————————————————————
   Each block is the app's real identity, lifted from the app itself.
   ———————————————————————————————————————————————————————— */

/* Eustin Eats — dark walnut, ivory card, lamp amber */
.panel-eats {
  --bg: #221812;
  --fg: #f3e7d3;
  --fg-muted: #c9b49b;
  --edge: #4a382a;
  --accent: #e8a63f;
  --face: "Young Serif", Georgia, serif;
}

/* Train — near-black glass under pace-blue, straight from its dark theme */
.panel-train {
  --bg: #0c0d10;
  --fg: #f2f4f7;
  --fg-muted: #9aa2ad;
  --edge: #2a2e35;
  --accent: #4a90ff;
  --face: var(--face-mono);
  --face-weight: 700;
  --face-tracking: -0.02em;
}

/* Fitpic — warm linen, ink, and clay */
.panel-fitpic {
  --bg: #f6f3ec;
  --fg: #191713;
  --fg-muted: #8c8578;
  --edge: #e7e1d4;
  --accent: #b3502d;
  --face: "Instrument Serif", Georgia, serif;
}

/* Tally — the walnut ledger and its amber ink */
.panel-tally {
  --bg: #211b16;
  --fg: #f5e9d4;
  --fg-muted: #cbb89e;
  --edge: #4a382a;
  --accent: #e8a63f;
  --face: Georgia, "Times New Roman", serif;
}

/* Adventure Jeopardy — slate-950 under a purple→cyan→lime rail */
.panel-jeopardy {
  --bg: #020617;
  --fg: #e2e8f0;
  --fg-muted: #94a3b8;
  --edge: #1e293b;
  --accent: #22d3ee;
  --face: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --face-weight: 700;
  --face-tracking: -0.03em;
}

/* Letter — newsprint and two inks. The one light panel on the shelf. */
.panel-letter {
  --bg: #f4f1ea;
  --fg: #14110d;
  --fg-muted: #5b544b;
  --edge: #14110d;
  --accent: #e5484d;
  --face: "Archivo Black", Impact, sans-serif;
  --face-case: uppercase;
  --face-tracking: -0.005em;
}
/* the zine's hard letterpress edge, instead of the room's soft glow */
.panel-letter { border-width: 2px; }
/* the stamp is full-bleed and solid, so it reads heavier than the other
   three marks at the same box size — trim it back to match optically */
.panel-letter .mark svg { width: 40px; height: 40px; }
.panel-letter::before { opacity: 0.07; }
/* Letter's own shadow is ink-on-paper, which is invisible against the
   dark shelf — so the offset here is a second sheet of newsprint
   instead, which reads as the same letterpress gesture in this room. */
.panel-letter:is(:hover, :focus-visible) {
  box-shadow: 6px 6px 0 rgb(244 241 234 / 0.17);
  --lift: -3px;
}
.panel-letter:is(:hover, :focus-visible)::before { opacity: 0.13; }

/* Wine — cellar dark, a claret pour, and a gold rim */
.panel-wine {
  --bg: #0d0a0b;
  --fg: #ece5df;
  --fg-muted: #9c8f89;
  --edge: #2a2224;
  --accent: #c8a15a;
  --face: "Cormorant Garamond", Georgia, serif;
  --face-weight: 600;
}

/* Coffee — espresso and crema. Held much darker than Eats so the two
   warm tiles never read as the same panel twice. */
.panel-coffee {
  --bg: #18120f;
  --fg: #e3d2c0;
  --fg-muted: #9c8571;
  --edge: #3a2b22;
  --accent: #d8b48a;
  --face: "Young Serif", Georgia, serif;
}

/* ——— the bio page ——————————————————————————————————————
   One column of prose in the same quiet room — the builder's page
   borrows nothing from the apps except the shelf they sit on. */

.bio {
  max-width: 620px;
  margin: 0 auto;
  display: grid;
  gap: clamp(20px, 4vw, 32px);
}

.bio-card {
  padding: clamp(22px, 4vw, 32px);
  border: 1px solid var(--room-edge);
  border-radius: var(--radius);
  background: radial-gradient(90% 70% at 15% 0%, #201a16 0%, transparent 75%);
}
.bio-card p { margin: 0 0 1em; }
.bio-card p:last-child { margin-bottom: 0; }
.bio-card strong { color: var(--room-fg); font-weight: 600; }
.bio-card { color: var(--room-muted); }

.bio-head {
  margin: 0 0 16px;
  font-family: var(--face-mono);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--room-muted);
}

.bio-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
}
.bio-list li {
  padding: 12px 0;
  border-top: 1px solid var(--room-edge);
  font-size: var(--fs-sm);
  color: var(--room-muted);
  line-height: 1.6;
}
.bio-list li:last-child { border-bottom: 1px solid var(--room-edge); }

.bio-what {
  display: inline-block;
  min-width: 88px;
  margin-right: 10px;
  font-family: var(--face-mono);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #6f625a;
}

.bio-list a {
  color: var(--room-fg);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--room-edge);
  transition: border-color 220ms var(--ease);
}
.bio-list a:is(:hover, :focus-visible) { border-color: var(--room-fg); }

/* ——— the footer ————————————————————————————————————————— */

.footer {
  max-width: 860px;
  margin: clamp(36px, 6vw, 56px) auto 0;
  text-align: center;
}

/* ——— reduced motion ————————————————————————————————————— */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .panel { --lift: 0px !important; }
}
