:root {
  --paper:  #faf9f6;
  --ink:    #14181a;
  --mid:    #5b6560;
  --faint:  #9aa39d;
  --rule:   #dedbd2;
  --hair:   #ebe8e0;
  --accent: #0f6f4d;

  --tile:   #ffffff;
  --panel:  #ffffff;
  --shade:  rgba(20,24,26,.05);

  --c-learn:    #2f5fb5;
  --c-contract: #1d6f8f;
  --c-fail:     #b4472e;
  --c-check:    #0f6f4d;
  --c-stock:    #7a4fa3;
  --c-fin:      #a8681d;
  --c-bill:     #0f766e;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --sans:  ui-sans-serif, -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
  --mono:  ui-monospace, SFMono-Regular, Menlo, "Cascadia Mono", monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #12140f;
    --ink:   #ece9df;
    --mid:   #9ba39a;
    --faint: #6d7670;
    --rule:  #33362c;
    --hair:  #24271f;
    --accent:#5cc99a;
    --tile:  #191c15;
    --panel: #171a13;
    --shade: rgba(0,0,0,.5);

    --c-learn:    #7ba4e8;
    --c-contract: #6fb6d4;
    --c-fail:     #e58a70;
    --c-check:    #5cc99a;
    --c-stock:    #b791d9;
    --c-fin:      #d9a663;
    --c-bill:     #58bdb0;
  }
}

* { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.45 var(--serif);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

.skip { position: absolute; left: -9999px; background: var(--accent); color: #fff; padding: .7rem 1rem; border-radius: 6px; z-index: 30; }
.skip:focus { left: 1rem; top: 1rem; }

/* The page is locked to the viewport. Nothing scrolls: twelve tiles in three
   groups fit one screen, and every vertical measure is a clamp against vh so
   the layout compresses on a short laptop rather than spilling past the fold. */
.page {
  width: min(1340px, 100% - 3rem);
  margin-inline: auto;
  height: 100dvh;
  padding: clamp(1.1rem, 3vh, 2.6rem) 0 0;
  display: flex;
  flex-direction: column;
}
@media (max-width: 640px) { .page { width: min(1340px, 100% - 1.6rem); } }

/* ── masthead ─────────────────────────────────────────────────────────── */

.mast {
  flex: none;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 2rem;
  padding-bottom: clamp(.6rem, 1.6vh, 1.2rem);
  border-bottom: 2px solid var(--ink);
}

.mast-id { display: flex; align-items: flex-end; gap: clamp(.6rem, 1.4vw, 1rem); }

.mark {
  width: clamp(32px, 5vh, 48px); height: clamp(32px, 5vh, 48px);
  object-fit: contain; border-radius: 9px; margin-bottom: .28rem; flex: none;
}

.mast h1 {
  margin: 0;
  font-size: clamp(1.9rem, 5.2vh, 3.9rem);
  line-height: .88; letter-spacing: -.038em; font-weight: 600;
}
.mast h1 sup { font-size: .4em; top: -.78em; position: relative; }

.mast-meta {
  display: flex; flex-direction: column; text-align: right;
  font-family: var(--mono); font-size: .66rem; letter-spacing: .11em;
  text-transform: uppercase; color: var(--mid); line-height: 1.95;
}
.mast-meta .host { color: var(--faint); }
@media (max-width: 620px) { .mast-meta { display: none; } }

/* ── body: directory + preview ────────────────────────────────────────── */

.body {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(320px, 38vw, 580px);
  gap: clamp(1.2rem, 3vw, 2.6rem);
}

.col-left { min-height: 0; display: flex; flex-direction: column; }

.tagline {
  flex: none;
  font-style: italic; color: var(--mid);
  font-size: clamp(.86rem, 1.7vh, 1.02rem);
  margin: clamp(.6rem, 1.7vh, 1rem) 0 clamp(.7rem, 1.8vh, 1.3rem);
  max-width: 54ch;
}
/* The first thing worth losing to keep all twelve tiles above the fold. */
@media (max-height: 720px) { .tagline { display: none; } }

/* ── search ───────────────────────────────────────────────────────────── */

.find {
  flex: none;
  display: flex; align-items: center; gap: .7rem;
  border-bottom: 1px solid var(--rule);
  padding: .4rem .1rem;
  transition: border-color .16s;
}
.find:focus-within { border-bottom-color: var(--accent); }
.find svg { width: 16px; height: 16px; flex: none; fill: none; stroke: var(--faint); stroke-width: 2; stroke-linecap: round; }
.find:focus-within svg { stroke: var(--accent); }
.find input {
  flex: 1; border: 0; outline: 0; background: none; color: inherit;
  font: 400 clamp(.92rem, 1.8vh, 1.02rem)/1.4 var(--serif);
  padding: .25rem 0; min-height: 38px; min-width: 0;
}
.find input::placeholder { color: var(--faint); font-style: italic; }
.find input::-webkit-search-cancel-button { display: none; }
.find kbd {
  font: 500 .66rem/1 var(--mono); color: var(--faint);
  border: 1px solid var(--rule); border-radius: 4px; padding: .28rem .4rem; flex: none;
}
@media (max-width: 620px) { .find kbd { display: none; } }

/* ── directory ────────────────────────────────────────────────────────── */

/* Flex, not grid: `auto` grid rows shrink below their content when the
   container has a definite height, which silently overlaps the sections. */
#directory {
  flex: 1; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
  gap: clamp(.6rem, 2.2vh, 1.7rem);
  padding-top: clamp(.8rem, 2.4vh, 1.7rem);
}

.sec { flex: none; }

.sec-h {
  display: flex; align-items: baseline; gap: .85rem;
  margin: 0 0 clamp(.5rem, 1.6vh, 1.2rem); padding-bottom: .35rem;
  border-bottom: 1px solid var(--hair);
}
.sec-h h2 { margin: 0; font-size: clamp(.9rem, 1.85vh, 1.14rem); font-weight: 600; letter-spacing: -.015em; }
.sec-h .n { font-family: var(--mono); font-size: .64rem; letter-spacing: .13em; color: var(--faint); text-transform: uppercase; }
.sec-h::after { content: ""; flex: 1; }

/* ── tiles ────────────────────────────────────────────────────────────── */

.tiles {
  display: grid;
  gap: clamp(.6rem, 1.7vh, 1.2rem) clamp(.5rem, 1.2vw, 1rem);
  grid-template-columns: repeat(auto-fill, minmax(clamp(84px, 8vw, 112px), 1fr));
}

.tile {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(.3rem, .9vh, .6rem);
  text-decoration: none; color: inherit;
  background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
}

.sq {
  width: clamp(44px, 6.8vh, 66px);
  height: clamp(44px, 6.8vh, 66px);
  border-radius: clamp(10px, 1.7vh, 16px);
  display: grid; place-items: center;
  background: var(--tile);
  border: 1px solid var(--rule);
  box-shadow: 0 1px 2px var(--shade), 0 8px 18px -15px var(--shade);
  transition: transform .17s ease, box-shadow .17s ease, border-color .17s ease;
}
.tile:hover .sq, .tile:focus-visible .sq {
  transform: translateY(-3px);
  border-color: currentColor;
  box-shadow: 0 2px 4px var(--shade), 0 18px 28px -18px var(--shade);
}
.tile:focus-visible { outline: none; }
.tile:focus-visible .sq { outline: 2px solid var(--accent); outline-offset: 3px; }
.tile[aria-current="true"] .sq {
  border-color: currentColor;
  box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 30%, transparent);
}

.sq svg {
  width: clamp(20px, 3.1vh, 29px); height: clamp(20px, 3.1vh, 29px);
  fill: none; stroke: currentColor; stroke-width: 1.65;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Two lines' worth of height whether the name wraps or not, so the status
   labels below sit on one baseline across the row. */
.tile .nm {
  font-size: clamp(.74rem, 1.5vh, .92rem); text-align: center; line-height: 1.22;
  letter-spacing: -.012em; color: var(--ink); max-width: 13ch; min-height: 2.44em;
}
.tile .st {
  font-family: var(--mono); font-size: clamp(.5rem, .9vh, .58rem);
  letter-spacing: .12em; text-transform: uppercase; color: var(--faint);
}
.tile .st.up { color: var(--accent); }
.tile.soon { opacity: .5; }
.tile.soon .sq { box-shadow: none; background: transparent; border-style: dashed; }

.empty { color: var(--faint); font-style: italic; }

/* ── preview panel ────────────────────────────────────────────────────── */

.preview {
  min-height: 0; position: relative;
  border-left: 1px solid var(--hair);
  padding: clamp(.8rem, 2.4vh, 1.7rem) 0 0 clamp(1.1rem, 2.4vw, 2.1rem);
  display: flex; flex-direction: column;
}

.preview-body { display: flex; flex-direction: column; min-height: 0; height: 100%; }

.preview-close {
  position: absolute; top: .6rem; right: 0;
  background: none; border: 0; padding: .4rem; cursor: pointer; color: var(--faint);
  line-height: 0; border-radius: 6px;
}
.preview-close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.preview-close:hover { color: var(--ink); }

/* resting state */
.hint {
  margin: auto 0; color: var(--faint); font-style: italic;
  font-size: clamp(.86rem, 1.7vh, .98rem); line-height: 1.55; max-width: 30ch;
}

/* selected state */

/* A live iframe rather than a captured thumbnail: none of these apps send
   X-Frame-Options or frame-ancestors, so the panel can show what is actually
   deployed right now instead of a screenshot that silently goes stale. The
   frame is rendered at desktop width and scaled down, so the miniature is the
   real desktop layout rather than the app's mobile breakpoint. */
/* Takes every pixel the panel is not using for text: scaled to the panel's
   width, then as tall as what is left, so a taller window shows more of the
   page rather than more empty panel. */
.pv-shot {
  flex: 1 1 auto; position: relative;
  width: 100%; min-height: clamp(150px, 24vh, 300px);
  border: 1px solid var(--rule); border-radius: 10px;
  overflow: hidden; background: var(--tile);
  margin-bottom: clamp(.7rem, 1.9vh, 1.1rem);
}
.pv-shot iframe {
  position: absolute; top: 0; left: 0;
  width: 1280px; height: 800px; border: 0;
  transform: scale(var(--shot-scale, .25));
  transform-origin: top left;
  pointer-events: none;
}
.pv-shot .shot-state {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--mono); font-size: .6rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint);
  background: var(--tile);
  transition: opacity .3s;
}
.pv-shot.ready .shot-state { opacity: 0; pointer-events: none; }
.pv-shot.blank {
  border-style: dashed;
  background: repeating-linear-gradient(
    -45deg, transparent, transparent 7px, var(--hair) 7px, var(--hair) 8px);
}

.pv-head { display: flex; align-items: center; gap: .7rem; margin-bottom: .4rem; }

.pv-mark {
  width: clamp(34px, 4.6vh, 44px); height: clamp(34px, 4.6vh, 44px);
  border-radius: clamp(9px, 1.3vh, 12px);
  display: grid; place-items: center; flex: none;
  background: var(--tile); border: 1px solid var(--rule);
}
.pv-mark svg {
  width: clamp(17px, 2.3vh, 22px); height: clamp(17px, 2.3vh, 22px);
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

.pv-name {
  margin: 0; font-size: clamp(1.05rem, 2.3vh, 1.4rem);
  letter-spacing: -.022em; font-weight: 600; line-height: 1.12;
}
.pv-desc {
  margin: 0 0 clamp(.8rem, 2.2vh, 1.3rem); color: var(--mid);
  font-size: clamp(.86rem, 1.65vh, .97rem); line-height: 1.5;
}

.pv-meta { flex: none; margin: 0; border-top: 1px solid var(--hair); }
.pv-meta div {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: clamp(.4rem, 1.1vh, .6rem) 0;
  border-bottom: 1px solid var(--hair);
  font-family: var(--mono); font-size: clamp(.6rem, 1.15vh, .68rem);
  letter-spacing: .07em; text-transform: uppercase;
}
.pv-meta dt { color: var(--faint); }
.pv-meta dd { margin: 0; color: var(--mid); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv-meta dd.up { color: var(--accent); }

.pv-go {
  flex: none; display: flex; align-items: center; justify-content: center; gap: .55rem;
  margin: clamp(.9rem, 2.4vh, 1.4rem) 0 clamp(.4rem, 1.4vh, .9rem);
  padding: .8rem 1.1rem; min-height: 46px;
  background: var(--accent); color: var(--paper);
  border: 0; border-radius: 9px;
  font: 500 clamp(.86rem, 1.6vh, .95rem)/1 var(--sans);
  letter-spacing: -.005em; text-decoration: none; cursor: pointer;
  transition: filter .15s;
}
.pv-go:hover { filter: brightness(1.09); }
.pv-go svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; }

.pv-go.off {
  background: none; color: var(--faint);
  border: 1px dashed var(--rule); cursor: not-allowed;
}
.pv-go.off:hover { filter: none; }

/* ── footer ───────────────────────────────────────────────────────────── */

.colophon {
  flex: none; margin-top: 0; padding: clamp(.6rem, 1.6vh, .95rem) 0;
  border-top: 2px solid var(--ink);
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  font-family: var(--mono); font-size: .62rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint);
}

/* ── narrow: preview becomes a sheet ──────────────────────────────────── */

@media (max-width: 860px) {
  .body { grid-template-columns: minmax(0, 1fr); }

  /* On a phone the twelve tiles genuinely do not fit one screen — four per
     row across three groups needs more height than any handset has. Keeping
     overflow hidden here just amputates the Finance group. So the page stays
     locked (masthead and footer never move) and the directory alone scrolls.
     Desktop keeps the no-scroll layout, where everything really does fit. */
  #directory {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding-bottom: 1rem;
  }

  /* Buys back a row of tiles; the page title already says what this is. */
  .tagline { display: none; }

  .preview {
    position: fixed; inset: auto 0 0 0; z-index: 20;
    background: var(--panel);
    border-left: 0; border-top: 1px solid var(--rule);
    border-radius: 16px 16px 0 0;
    padding: 1.3rem 1.3rem calc(1.3rem + env(safe-area-inset-bottom));
    box-shadow: 0 -12px 40px -18px rgba(0,0,0,.45);
    transform: translateY(101%);
    transition: transform .24s cubic-bezier(.32,.72,0,1);
    max-height: 78dvh;
  }
  .preview.open { transform: translateY(0); }
  .preview .hint { display: none; }
  .pv-meta { margin-bottom: 0; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .tile:hover .sq { transform: none; }
}

.pv-sub {
  display: block;
  font-family: var(--mono); font-size: clamp(.56rem, 1.05vh, .64rem);
  letter-spacing: .11em; text-transform: uppercase; color: var(--faint);
  margin-top: .2rem;
}
