/* udincloud.me: shared shell.
   A terminal session, rebuilt for the web. Palette and the ember accent carry
   over from music.html so the whole domain reads as one place.

   Three patterns here sit next to the Hard Gate and are recorded in DESIGN.md
   with the rule numbers and the job each one does: the 4% background grid
   (R-07), the mono typeface (R-06), and the dose-capped glow (R-01/R-13).
   Everything else in the slop list is absent on purpose. */

:root {
  --surface: #08090b;
  --raised: #0e1014;
  --line: #1e2229;
  --line-hot: #2b313b;
  --ink: #e7e9ec;
  --dim: #8b929e;
  --faint: #78808c;
  --green: #4ee08a;
  --green-dim: #2f8f5b;
  --ember: #ff5c38;

  --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, monospace;
  --serif: 'Fraunces', Georgia, 'Times New Roman', serif;

  --pad: 20px;
  --rail: 26px;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 14.5px;
  line-height: 1.62;
  overflow-x: hidden;
  /* The terminal's own texture: a 32px cell grid at 4% opacity. Nearly
     invisible by design, which is why it is the one pure decoration allowed. */
  background-image:
    linear-gradient(rgba(78, 224, 138, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(78, 224, 138, 0.04) 1px, transparent 1px);
  background-size: 32px 32px;
}

@media (min-width: 900px) { body { font-size: 15px; } }

a { color: var(--ink); text-decoration: none; }
a:focus-visible, .tool:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: rgba(78, 224, 138, 0.28); }

/* Scroll progress: one hairline, a position cue. */
#progress {
  position: fixed;
  top: 0; left: 0;
  height: 2px; width: 0;
  background: var(--green);
  box-shadow: 0 0 10px rgba(78, 224, 138, 0.55);
  z-index: 60;
  transition: width 90ms linear;
}

/* ── Page frame ────────────────────────────────────────────────────────── */

.page {
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(40px, 8vh, 74px) var(--pad) 96px;
  padding-left: calc(var(--pad) + var(--rail));
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 44px;
  align-items: start;
}
/* The rain runs behind everything, so a bright glyph can land directly under a
   line of text and drop its contrast below AA. This backing plate keeps the
   rain as atmosphere around the content rather than noise underneath it, and
   the alpha is high enough that the worst-case glyph under text is still
   dominated by the plate. */
.transcript {
  min-width: 0;
  position: relative;
  background: rgba(8, 9, 11, .93);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: clamp(20px, 3.4vw, 40px) clamp(18px, 3vw, 38px);
}
@media (max-width: 720px) {
  .transcript { background: rgba(8, 9, 11, .9); border-radius: 3px; }
}

/* The rain sits behind everything and never takes a click. */
#rain {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
  opacity: .62;
  pointer-events: none;
}

@media (min-width: 1180px) {
  .page {
    /* auto, not 1fr: the transcript's own lines are capped for readability, so
       a 1fr column would leave the slack inside the column as empty space
       again. Sizing to content and centring the pair is what actually uses
       the width. */
    grid-template-columns: minmax(0, auto) 320px;
    column-gap: 64px;
    justify-content: center;
    max-width: 1180px;
  }
  .panel { position: sticky; top: clamp(40px, 8vh, 74px); }
}

/* A fixed gutter of line numbers in the terminal's own voice. Hidden on small
   screens where there is no room for it. */
.rail {
  position: fixed;
  top: 0; left: 0;
  width: var(--rail);
  height: 100%;
  padding-top: clamp(40px, 8vh, 74px);
  padding-right: 9px;
  text-align: right;
  font-size: 10.5px;
  line-height: 1.62;
  color: var(--faint);
  user-select: none;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
}
@media (max-width: 720px) {
  .rail { display: none; }
  .page { padding-left: var(--pad); }
}

/* ── The prompt ────────────────────────────────────────────────────────── */

.prompt {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 8px;
  margin: 0 0 6px;
  font-size: 0.95rem;
}
.ps1 { color: var(--green); font-weight: 600; white-space: nowrap; }
.ps1 .at { color: var(--green-dim); }
.ps1 .path { color: var(--dim); }
.cmd { color: var(--ink); }
.caret {
  display: inline-block;
  width: 8px;
  height: 1.05em;
  background: var(--green);
  box-shadow: 0 0 8px rgba(78, 224, 138, 0.7);
  transform: translateY(2px);
  animation: blink 1.06s step-end infinite;
}
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

/* ── Output ────────────────────────────────────────────────────────────── */

.out {
  margin: 0 0 10px;
  color: var(--dim);
  max-width: 62ch;
}
.out strong { color: var(--ink); font-weight: 600; }
.comment { color: var(--faint); }

.masthead { margin-bottom: 44px; }

.name {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(2.1rem, 6.4vw, 3.2rem);
  line-height: 1.04;
  letter-spacing: -0.012em;
  margin: 2px 0 10px;
  color: var(--ink);
}

/* The serif name is deliberately the only non-mono voice, and this line says
   why out loud, so it reads as a person at a terminal and not as a mistake. */
.name-note { margin-bottom: 20px; }

.claim {
  margin: 0 0 18px;
  max-width: 56ch;
  color: var(--dim);
  font-size: 0.95rem;
}

.elsewhere {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
}

/* The mark carries the identity, the label carries the meaning. Same palette as
   the rest of the page: no brand colours, so the row belongs to this page
   rather than to four other companies. */
.elsewhere a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px 7px 10px;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--dim);
  background: rgba(14, 16, 20, .55);
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease, transform 140ms ease;
}
.mark { width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; }
.mark-name { font-size: .84rem; letter-spacing: .01em; }
.elsewhere a:hover,
.elsewhere a:focus-visible {
  color: var(--green);
  border-color: var(--green-dim);
  background: rgba(78, 224, 138, .07);
  transform: translateY(-2px);
}
.elsewhere a::before { content: './'; color: var(--faint); }

/* ── Sections ──────────────────────────────────────────────────────────── */

.block { margin-top: 42px; }
.block:first-of-type { margin-top: 0; }

.block-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
}
h2 {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--ink);
}
h2 .dir { color: var(--green-dim); }
.tally { color: var(--faint); font-size: 0.82rem; white-space: nowrap; }

/* ── Tool rows: the interactive summit of the page ─────────────────────── */

.tools { list-style: none; margin: 0; padding: 0; }
.tools li + li { margin-top: 6px; }

.tool {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 4px 16px;
  padding: 15px 16px 15px 14px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: transparent;
  min-width: 0;
  position: relative;
  overflow: hidden;
  transition: border-color 160ms ease, background 160ms ease, transform 160ms ease;
}
/* The accent edge is a bar that wipes in from the top, not a static coloured
   strip, so it reads as the row waking up under the pointer. */
.tool::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--green);
  box-shadow: 0 0 10px rgba(78, 224, 138, 0.6);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 180ms ease;
}
.tool:hover, .tool:focus-visible {
  background: var(--raised);
  border-color: var(--line-hot);
  transform: translateX(3px);
}
.tool:hover::before, .tool:focus-visible::before { transform: scaleY(1); }

.tool-body { min-width: 0; }

.tool-name {
  display: block;
  font-weight: 600;
  font-size: 1rem;
  margin-bottom: 4px;
  color: var(--ink);
}
.tool-name::before { content: '$ '; color: var(--green-dim); font-weight: 400; }
.tool-what {
  margin: 0;
  color: var(--dim);
  font-size: 0.88rem;
  max-width: 60ch;
}
.tool-what code {
  font: inherit;
  color: var(--green);
  background: rgba(78, 224, 138, 0.08);
  padding: 1px 5px;
  border-radius: 2px;
}
.tool-go {
  color: var(--faint);
  font-size: 0.82rem;
  white-space: nowrap;
  transition: color 160ms ease, transform 160ms ease;
}
.tool:hover .tool-go, .tool:focus-visible .tool-go { color: var(--green); transform: translateX(2px); }

/* ── Work entries: commit-like, deliberately uneven ───────────────────── */

.work { list-style: none; margin: 0; padding: 0; }

.work li {
  border-top: 1px solid var(--line);
  padding: 18px 0 18px 16px;
  position: relative;
  min-width: 0;
}
.work li::before {
  content: '*';
  position: absolute;
  left: 0;
  top: 18px;
  color: var(--green-dim);
  font-size: 0.9rem;
}
.work li:last-child { border-bottom: 1px solid var(--line); }

.work-name {
  display: inline-block;
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--ink);
  margin-bottom: 5px;
  border-bottom: 1px solid transparent;
  transition: border-color 150ms ease, color 150ms ease;
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
}
.work-name:hover { color: var(--green); border-bottom-color: var(--green); }

.work-why {
  margin: 0 0 9px;
  color: var(--dim);
  font-size: 0.88rem;
  max-width: 66ch;
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.79rem;
  color: var(--faint);
}
.meta li { white-space: nowrap; }
.meta li + li::before { content: '· '; color: var(--line-hot); }

/* The one item allowed more room than the rest: it is the strongest piece. */
.work li[data-weight="lead"] .work-name { font-size: 1.14rem; }
.work li[data-weight="lead"] .work-why { font-size: 0.95rem; }

/* ── Coursework: a dense index, quiet on purpose ──────────────────────── */

.minor {
  list-style: none;
  margin: 0;
  padding: 0;
  columns: 2;
  column-gap: 26px;
  font-size: 0.85rem;
}
.minor li {
  break-inside: avoid;
  padding: 3px 0;
  color: var(--faint);
  min-width: 0;
}
.minor a { color: var(--dim); }
.minor a:hover { color: var(--green); }

/* ── States inside the fetched block ──────────────────────────────────── */

.state {
  margin: 0;
  padding: 16px 0;
  color: var(--dim);
  font-size: 0.88rem;
}
.state a { color: var(--green); border-bottom: 1px solid var(--green-dim); }
.state.err { color: var(--ember); }
.state.err a { color: var(--ember); border-bottom-color: var(--ember); }

.skeleton { list-style: none; margin: 0; padding: 0; }
.skeleton li {
  padding: 18px 0;
  border-top: 1px solid var(--line);
  min-width: 0;
}
.skeleton i {
  display: block;
  height: 11px;
  border-radius: 2px;
  background: linear-gradient(90deg, #14171c 0%, #1d222a 50%, #14171c 100%);
  background-size: 200% 100%;
  animation: sweep 1.35s linear infinite;
}
@keyframes sweep { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.skeleton .w1 { width: 42%; margin-bottom: 9px; }
.skeleton .w2 { width: 74%; margin-bottom: 7px; }
.skeleton .w3 { width: 30%; }

/* Says when the committed snapshot was taken. Quiet, because a stale list is
   not a problem, it just should not be a surprise. */
.stamp { margin: 6px 0 0; font-size: .76rem; color: var(--faint); }
.stamp:empty { display: none; }

/* ── Status panel ────────────────────────────────────────────────────────
   Reads as `htop` cut down to four honest lines. It only exists when there is
   room for it; below that the transcript stands alone rather than being
   squeezed. No graphs, because there is nothing real to graph. */

.panel {
  border: 1px solid var(--line);
  border-radius: 3px;
  background: rgba(14, 16, 20, .72);
  padding: 16px 16px 14px;
  min-width: 0;
}
.panel-head {
  font-family: var(--mono);
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 12px;
}
.panel-grid { margin: 0; display: grid; gap: 9px; }
.panel-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: baseline;
  min-width: 0;
}
.panel-row dt { color: var(--faint); font-size: .78rem; }
.panel-row dd {
  margin: 0;
  color: var(--ink);
  font-size: .78rem;
  text-align: right;
  min-width: 0;
  overflow-wrap: anywhere;
}
.panel-foot {
  margin: 13px 0 0;
  padding-top: 11px;
  border-top: 1px solid var(--line);
  font-size: .73rem;
  color: var(--faint);
  line-height: 1.5;
}
.panel-foot:empty { display: none; }
/* Green is the live-state colour everywhere else, so it is the live-state
   colour here too, and only on the value that is actually live. */
.panel-row dd.on { color: var(--green); }

/* ── Entrance motion ───────────────────────────────────────────────────────
   One CSS animation, triggered by adding .enter from JS. There is no hidden
   default state anywhere: if the class is never added, or the animation never
   runs, or the whole script fails, every block is simply already in its final,
   visible state. Content is never contingent on motion. */

@keyframes rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
@keyframes slide {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

/* `backwards` and `forwards` are deliberately NOT used here. With `both`, a
   block that never gets animated stays at the keyframe's first frame, which is
   opacity 0, and the content disappears. With the default fill, the resting
   state of every element is its normal, visible self, and the animation only
   borrows it for 620ms. Nothing can be lost to an animation that does not run. */
.enter { animation: rise 620ms cubic-bezier(.2,.7,.3,1); animation-delay: var(--d, 0ms); }
.casc > .enter { animation: slide 460ms cubic-bezier(.2,.7,.3,1); animation-delay: var(--d, 0ms); }

/* ── Closing prompt and footer ────────────────────────────────────────── */

.end { margin-top: 54px; }

.foot {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 0.8rem;
}

@media (max-width: 520px) {
  .page { padding-top: 38px; }
  .minor { columns: 1; }
  .tool { grid-template-columns: 1fr; }
  .tool-go { grid-column: 1; }
}

/* Everything above collapses to a complete, static page. */
@media (prefers-reduced-motion: reduce) {
  #rain { display: none; }
  .caret { animation: none; }
  .skeleton i { animation: none; background: #14171c; }
  #progress { transition: none; }
  .enter { animation: none; }
}
