/* Tokens lifted from packages/homepage-web/src/style.css so this site and
   otty.sh are visibly one product. Light only, matching the approved design. */
:root {
  --paper: #ffffff;
  --ink: #0a0a0a;
  --muted: rgb(126, 127, 149);
  --chip: rgb(238, 240, 241);
  --chip-hover: rgb(228, 231, 233);
  --border: rgba(10, 10, 10, 0.08);
  --border-strong: rgba(10, 10, 10, 0.16);
  --label: rgba(10, 10, 10, 0.42);
  --hover: rgba(10, 10, 10, 0.035);

  /* Rule kinds. Drawn from the accent colors already used on otty.sh rather
     than invented: the brand palette itself is neutral. */
  --secret: #d04545;
  --secret-field: #d97757;
  --machine: #8957e5;

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
}

* { box-sizing: border-box; }

html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
[id] { scroll-margin-top: 24px; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-feature-settings: 'cv11', 'ss01', 'ss03';
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--muted); }

.wrap { max-width: 1440px; margin: 0 auto; padding: 0 64px; }
@media (max-width: 900px) { .wrap { padding: 0 24px; } }

/* ---------- Header ---------- */
.nav {
  display: flex; align-items: center; justify-content: space-between;
  height: 64px; border-bottom: 1px solid var(--border);
}
/* Two targets, not one: the mark and wordmark go to otty.sh, the suffix goes
   to this site's own root. Each says so on hover, and differently — leaving
   the site dims the whole mark as one object, while staying on it brightens
   the suffix in place. */
.brand { display: flex; align-items: center; gap: 8px; }
.brand-home {
  display: flex; align-items: center; gap: 9px;
  transition: opacity .12s ease;
}
.brand-home:hover { opacity: 0.62; }
.brand-name { font-size: 15px; font-weight: 600; letter-spacing: -0.02em; }
/* The generic a:hover greys every link; the wordmark dims with its mark
   instead, so hold its colour. */
.brand-home:hover .brand-name { color: var(--ink); }
.brand-sub {
  font-family: var(--mono); font-size: 12px; color: var(--label);
  transition: color .12s ease;
}
.brand-sub:hover { color: var(--ink); }
.nav-links { display: flex; align-items: center; gap: 28px; font-size: 13px; color: var(--muted); }
@media (max-width: 700px) { .nav-links a:not(.nav-gh) { display: none; } }

/* ---------- Hero ---------- */
.eyebrow {
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.18em; color: var(--label);
}
h1 {
  margin: 18px 0 0; font-size: 32px; font-weight: 500; line-height: 1.25;
  letter-spacing: -0.02em; max-width: 720px; text-wrap: pretty;
}
.lead {
  margin: 14px 0 0; max-width: 620px; font-size: 16px; line-height: 1.5;
  color: var(--muted); text-wrap: pretty;
}
.stats {
  display: flex; flex-wrap: wrap; gap: 32px; margin-top: 26px;
  font-family: var(--mono); font-size: 12px; color: var(--muted);
}
.stats b { font-weight: 500; color: var(--ink); }

/* ---------- Toolbar ---------- */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.search { position: relative; flex-grow: 1; max-width: 340px; }
.search svg { position: absolute; left: 15px; top: 12px; }
.search input {
  width: 100%; height: 40px; padding: 0 16px 0 40px;
  border: 1px solid var(--border); border-radius: 9999px;
  background: var(--paper); font-family: inherit; font-size: 13px; color: var(--ink);
  transition: border-color .12s ease;
}
.search input::placeholder { color: var(--label); }
.search input:focus { outline: none; border-color: rgba(10, 10, 10, 0.28); }

.pills { display: flex; gap: 8px; }
.pill {
  height: 40px; padding: 0 18px; border: none; border-radius: 9999px;
  background: var(--chip); color: var(--ink);
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: -0.01em; cursor: pointer; transition: background-color .12s ease;
}
.pill:hover { background: var(--chip-hover); }
.pill[aria-pressed="true"] { background: var(--ink); color: var(--paper); }

.legend { display: flex; flex-wrap: wrap; gap: 18px; margin-left: auto; font-size: 12px; color: var(--muted); }
.legend span { display: inline-flex; align-items: center; gap: 7px; }
.dot { width: 7px; height: 7px; border-radius: 9999px; flex-shrink: 0; }

/* ---------- Catalog grid ---------- */
.group { margin-bottom: 40px; }
.group-head {
  display: flex; align-items: baseline; gap: 12px;
  padding-bottom: 14px; margin-bottom: 18px; border-bottom: 1px solid var(--border);
}
.group-head h2 { margin: 0; font-size: 18px; font-weight: 500; letter-spacing: -0.02em; }
.group-head .count { font-family: var(--mono); font-size: 12px; color: var(--label); }

.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 1100px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px) { .grid { grid-template-columns: minmax(0, 1fr); } }

/* A tool's logo comes from its own project, so its colours and aspect ratio
   are not ours to choose. The slot is a fixed square and the image is
   contained inside it; a tool with no logo gets the same square, drawn as an
   outline, so the grid stays on its rhythm. */
.icon {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 28px; height: 28px;
}
.icon img { display: block; width: 100%; height: 100%; object-fit: contain; }
.icon-none { border: 1px dashed var(--border-strong); border-radius: 7px; }

.card {
  display: block; padding: 16px 18px; border: 1px solid var(--border);
  border-radius: 10px; background: var(--paper);
  transition: background-color .12s ease, border-color .12s ease;
}
.card:hover { background: var(--hover); border-color: var(--border-strong); color: inherit; }
.card-top { display: flex; align-items: center; gap: 10px; }
.card-name { flex: 1 1 auto; min-width: 0; font-size: 15px; font-weight: 500; letter-spacing: -0.01em; }
/* Two lines, clamped: a card whose description runs long must not make its
   row of cards taller than the rest. */
.card-desc {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; margin: 10px 0 0; font-size: 12.5px; line-height: 1.45;
  color: var(--muted); text-wrap: pretty;
}
.card-plat { font-family: var(--mono); font-size: 10px; letter-spacing: 0.04em; color: var(--label); white-space: nowrap; }
.card-bins { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.bin {
  display: inline-flex; align-items: center; height: 20px; padding: 0 8px;
  border-radius: 4px; background: var(--chip);
  font-family: var(--mono); font-size: 11px; color: rgba(10, 10, 10, 0.6);
}
.card-meta { display: inline-flex; align-items: center; height: 20px; font-size: 11px; color: var(--label); }
.marks { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; min-height: 16px; }
.mark { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; }

.empty { padding: 60px 0 80px; text-align: center; }
.empty-title { font-size: 15px; font-weight: 500; }
.empty p { margin: 8px 0 0; font-size: 13px; color: var(--muted); }

/* ---------- App page ---------- */
.crumbs { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--label); }
.crumbs .here { color: var(--ink); }
/* The way back is the one thing in this line you can act on, so it is shaped
   like something you press. The negative margin pays back its own padding, so
   the text still starts on the page's left edge. */
.crumb-back {
  display: inline-flex; align-items: center; gap: 6px;
  margin-left: -9px; padding: 5px 9px; border-radius: 8px;
  transition: background-color .12s ease, color .12s ease;
}
.crumb-back svg { flex: none; }
.crumbs a.crumb-back:hover { background: var(--chip); color: var(--ink); }

.app-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 24px; margin-top: 18px; }
.app-lede { flex: 1 1 340px; min-width: 0; }
.app-title { display: flex; align-items: center; gap: 12px; }
.app-title h1 { margin: 0; }
.app-title .icon { width: 34px; height: 34px; }
.app-desc { margin: 12px 0 0; max-width: 60ch; font-size: 14px; line-height: 1.5; color: var(--muted); text-wrap: pretty; }
.app-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.id-chip {
  display: inline-flex; align-items: center; height: 22px; padding: 0 9px;
  border-radius: 5px; background: var(--chip);
  font-family: var(--mono); font-size: 11px; color: rgba(10, 10, 10, 0.6);
}
.app-facts { display: flex; flex-wrap: wrap; align-items: center; gap: 20px; margin-top: 14px; font-size: 13px; color: var(--muted); }
.app-facts .sep { color: var(--border-strong); }
.app-facts code { font-family: var(--mono); color: var(--ink); }
.app-facts a { text-decoration: underline; text-underline-offset: 3px; }
/* The tool's own site is the one link here that leaves for somewhere we do not
   control, and it opens in a new tab; the arrow says so before the click. Only
   the host is underlined — a rule struck through the arrow reads as a strike,
   not a link. */
.app-facts a.ext { display: inline-flex; align-items: center; gap: 5px; text-decoration: none; }
.app-facts a.ext span { text-decoration: underline; text-underline-offset: 3px; }
.app-facts a.ext svg { flex: none; opacity: .7; }
.app-facts a.ext:hover { color: var(--ink); }

.btn {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 20px;
  border: none; border-radius: 9999px; background: var(--chip); color: var(--ink);
  font-family: var(--mono); font-size: 13px; font-weight: 500;
  letter-spacing: -0.01em; white-space: nowrap; cursor: pointer;
  transition: background-color .12s ease, opacity .12s ease;
}
.btn:hover { background: var(--chip-hover); color: var(--ink); }
.btn-dark { background: var(--ink); color: var(--paper); }
.btn-dark:hover { background: var(--ink); color: var(--paper); opacity: .9; }

.section-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.section-head .titles { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.section-head h2 { margin: 0; font-size: 18px; font-weight: 500; letter-spacing: -0.02em; }
.section-head .sub { font-size: 13px; color: var(--muted); }

.seg { display: flex; padding: 3px; border-radius: 9999px; background: var(--chip); }
.seg button {
  height: 32px; padding: 0 18px; border: none; border-radius: 9999px;
  background: transparent; color: rgba(10, 10, 10, 0.6);
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: -0.01em; cursor: pointer; transition: background-color .12s ease, color .12s ease;
}
.seg button[aria-pressed="true"] { background: var(--paper); color: var(--ink); }

.rule-group { margin-bottom: 30px; }
.rule-head { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; margin-bottom: 14px; }
.rule-head .name { font-size: 14px; font-weight: 500; }
.rule-head .why { font-size: 13px; color: var(--muted); }

.panel { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.panel-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px; padding: 11px 18px; background: var(--chip);
  font-size: 12px; color: var(--muted);
}
.panel-head code, .panel-head .file { font-family: var(--mono); font-size: 12px; color: var(--ink); }
/* The files panel head carries a control, so its two lines of text stack into
   one block and the switch sits opposite them rather than competing with the
   subtitle for the same row. */
.panel-head-stack { align-items: center; padding: 9px 12px 9px 18px; }
.panel-titles { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.panel-sub { font-size: 12px; color: var(--muted); }
.seg-sm { padding: 2px; }
.seg-sm button { height: 26px; padding: 0 13px; font-size: 11px; }
.panel-note { padding: 11px 18px 13px; border-top: 1px solid var(--border); font-size: 12px; line-height: 1.5; color: var(--muted); }
.panel-note code { font-family: var(--mono); }

.row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px 24px; padding: 13px 18px; border-top: 1px solid var(--border);
}
.row:first-child { border-top: none; }
.row .path { font-family: var(--mono); font-size: 13px; }
.row .tag { font-size: 12px; color: var(--muted); }

.panel-secret { border-color: rgba(208, 69, 69, 0.22); background: rgba(208, 69, 69, 0.035); }
.panel-secret .row { border-top-color: rgba(208, 69, 69, 0.16); }
.panel-secret .panel-note { border-top-color: rgba(208, 69, 69, 0.16); }
.row-lock { display: flex; align-items: center; gap: 12px; justify-content: flex-start; }

.keypaths { display: flex; flex-wrap: wrap; gap: 8px; padding: 14px 18px; }
.keypath {
  display: inline-flex; align-items: center; height: 24px; padding: 0 10px;
  border-radius: 5px; font-family: var(--mono); font-size: 12px;
}
.keypath-secret { background: rgba(217, 119, 87, 0.1); color: #a4522f; }
.keypath-machine { background: rgba(137, 87, 229, 0.09); color: var(--machine); }

/* Fixture lines are `white-space: pre` inside a panel that clips, so the long
   ones (ssh's IdentityAgent path) need their own scroller. */
.snippet { padding: 14px 0; overflow-x: auto; }
.snip-line { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-right: 18px; border-left: 3px solid transparent; }
.snip-line code { padding-left: 15px; font-family: var(--mono); font-size: 13px; line-height: 22px; color: var(--muted); white-space: pre; }
.snip-line.held { background: rgba(137, 87, 229, 0.06); border-left-color: var(--machine); }
.snip-line.held code { color: var(--ink); }
.snip-line .held-tag { font-size: 11px; color: var(--machine); white-space: nowrap; }
.snip-gap { padding: 2px 0 2px 18px; font-family: var(--mono); font-size: 13px; line-height: 18px; color: var(--label); }

.none { padding: 15px 18px; border: 1px dashed rgba(10, 10, 10, 0.14); border-radius: 10px; font-size: 13px; color: var(--muted); }

.code { padding: 16px 0; background: var(--paper); overflow-x: auto; }
.code div { padding: 0 18px; font-family: var(--mono); font-size: 13px; line-height: 22px; white-space: pre; }
.code .comment { color: var(--label); }
.code .table { color: var(--machine); }
.code .cont { color: var(--muted); }
/* The Otty pane's one line is the deep link itself, so it reads as copyable
   text and still works as a click. */
.code a.deep { color: inherit; text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--border-strong); }
.code a.deep:hover { color: inherit; text-decoration-color: currentColor; }

/* ---------- Files view ---------- */
/* The trunk is drawn, not typed. Box-drawing characters only join across rows
   when the line height equals the glyph's own, and these rows want air; so
   each node paints its own elbow and the vertical run past its subtree, and
   the last child stops its run at the elbow to close the branch. `--d` is the
   node's depth, set on the element.

   Rows are full width and unindented — the indent lives in the row's own
   padding — so every verdict lines up down the right edge whatever the depth. */
/* No padding on the right: the rows carry their own, so the gutter after a
   verdict survives a row wide enough to scroll. A padding-right here would be
   the container's, and a row scrolled past it would end flush with the edge. */
.tree { padding: 12px 0 12px 18px; overflow-x: auto; font-family: var(--mono); font-size: 13px; }
.tree-node { position: relative; }
.tree-node::before, .tree-node::after {
  content: ''; position: absolute; left: calc((var(--d) - 1) * 18px + 8px);
}
/* The elbow, meeting the row at its midline. Each edge names its own colour:
   the `border-<side>` shorthand resets the colour it does not mention, so a
   shared `border-color` above would be overwritten with currentColor here and
   the trunk would draw in the text's near-black. */
.tree-node::before { top: 14px; width: 10px; border-top: 1px solid var(--border-strong); }
/* The vertical run, the full height of this node and everything under it, so
   one sibling's line begins exactly where the last one ended. */
.tree-node::after { top: 0; bottom: 0; border-left: 1px solid var(--border-strong); }
.tree-node:last-child::after { bottom: auto; height: 14px; }
/* A root has no parent to hang from. */
.tree > .tree-node::before, .tree > .tree-node::after { display: none; }

.tree-row {
  /* At least the full width, so a verdict sits on the right edge; wider when
     the row is, so a row long enough to scroll carries its own right gutter
     rather than spilling its text past a padding it has outgrown. */
  display: flex; align-items: center; gap: 0; width: max-content; min-width: 100%;
  padding: 3px 18px 3px calc(var(--d) * 18px);
  border: none; background: none; color: inherit; font: inherit;
  line-height: 22px; white-space: pre; text-align: left;
}
/* A row is a path, and a path is there to be copied. Buttons do not let their
   text be selected by default, so say otherwise here and let the click handler
   tell a drag that ended in a selection from a click meant to fold. */
.tree-row { -webkit-user-select: text; user-select: text; }
.tree-row.is-openable { cursor: pointer; }
/* Every row highlights, foldable or not: the row is the unit being read, and
   a file that cannot open still wants its verdict tied to its name across a
   wide gap. The trunk is drawn by positioned pseudo-elements, so it paints
   over this rather than being covered by it. */
.tree-row:hover { background: var(--hover); }
.tree-twist { width: 16px; color: var(--label); }
/* A leaf's middle dot is a speck; the caret is a filled triangle several times
   its area. At one colour the carets read as a darker column down the tree, so
   the caret is lightened until the two weigh the same to the eye. */
.tree-row.is-openable .tree-twist { color: rgba(10, 10, 10, 0.26); }
.tree-name { color: var(--ink); }
.tree-name.is-dim { color: var(--muted); }
.tree-tag { margin-left: auto; padding-left: 20px; font-size: 11px; white-space: nowrap; }
.tag-sync { color: var(--ink); }
.tag-partial { color: var(--machine); }
.tag-excluded { color: var(--muted); }
.tag-secret { color: var(--secret); }
/* A file's held-back lines: the same snippet the Rules view shows, indented
   one step past the row that owns it. */
.tree-detail { padding: 4px 0 14px calc((var(--d) + 1) * 18px + 8px); }
.tree-detail .snippet { padding: 0; }
.tree-detail .keypaths { padding: 2px 0 0; }
.tree-detail .panel-note { padding: 10px 0 0; margin: 10px 0 0; }

/* ---------- Install pane that is an action, not a command ---------- */
/* With the button in the panel head this pane is only its note, and it keeps
   the code panes' vertical rhythm so the panel does not resize between tabs. */
.pane-action { padding: 16px 18px; }
.pane-action-note { max-width: 62ch; margin: 0; font-size: 12px; line-height: 1.5; color: var(--muted); }
.pane-action-note code { font-family: var(--mono); color: var(--ink); }

.tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.tab {
  height: 36px; padding: 0 16px; border: 1px solid rgba(10, 10, 10, 0.12);
  border-radius: 9999px; background: var(--paper); color: var(--ink);
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: -0.01em; cursor: pointer; transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.tab:hover { background: var(--hover); }
.tab[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }

.copy {
  display: inline-flex; align-items: center; gap: 7px; height: 30px; padding: 0 14px;
  border: none; border-radius: 9999px; background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 12px; font-weight: 500; cursor: pointer;
  transition: background-color .12s ease, color .12s ease;
}
.copy[data-copied="true"] { background: var(--paper); color: var(--ink); box-shadow: inset 0 0 0 1px var(--border-strong); }
/* Two filled pills side by side compete; the Otty action is the primary one
   here, so Copy drops to the outline treatment the page already uses for
   secondary actions and inverts to the filled state to confirm a copy. */
.copy.ghost { background: none; color: var(--ink); box-shadow: inset 0 0 0 1px var(--border-strong); }
.copy.ghost:hover { background: var(--chip); }
.copy.ghost[data-copied="true"] { background: var(--ink); color: var(--paper); box-shadow: none; }
.panel-actions { display: flex; align-items: center; gap: 8px; }
a.copy { text-decoration: none; }

.caveat { display: flex; align-items: flex-start; gap: 10px; margin-top: 16px; max-width: 780px; font-size: 12px; line-height: 1.55; color: var(--muted); }
/* Louder than a caveat, because it changes what a reader should do rather
   than qualifying what they just read. It borrows the palette's warmest
   accent — the one that already means "handled differently" on this page. */
.callout {
  display: flex; align-items: flex-start; gap: 11px;
  margin-top: 26px; padding: 13px 16px; max-width: 900px;
  border: 1px solid rgba(217, 119, 87, 0.3); border-radius: 10px;
  background: rgba(217, 119, 87, 0.06);
  font-size: 13px; line-height: 1.55; color: var(--muted); text-wrap: pretty;
}
.callout svg { flex: none; margin-top: 2px; color: var(--secret-field); }
.callout b { font-weight: 500; color: var(--ink); }
.caveat svg { flex-shrink: 0; margin-top: 2px; }
.caveat code { font-family: var(--mono); color: var(--machine); }
.caveat a { text-decoration: underline; text-underline-offset: 3px; }

/* ---------- Footer ---------- */
.foot {
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: space-between; align-items: center;
  margin-top: 44px; padding: 28px 0 44px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--label);
}
.foot .host { font-family: var(--mono); }

[hidden] { display: none !important; }

/* The tail of the list. It carries a note while more is coming and, without an
   IntersectionObserver, becomes the control that asks for it. */
.more {
  display: flex; align-items: center; justify-content: center;
  padding: 28px 0 8px; cursor: pointer;
}
.more-note { font-size: 13px; color: var(--label); }
