/* ==========================================================================
   CTRL+ALT+DEL :: CARD FORGE
   Palette, tag shape and type all come from the finished card artwork:
   six game colours, paper-white tags, hexagonal banners, JetBrains Mono
   ExtraBold Italic.
   ========================================================================== */

/* Bungee ships in vendor/ but was never declared, so every use of it fell back to a
   system font -- which is why the cost and power digits sat off-centre: their
   optical nudge was tuned for Bungee's metrics, not the fallback's. */
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-latin-ext-400.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/jetbrains-mono/jetbrains-mono-400.woff2') format('woff2');
}
/* Bungee ships in vendor/ but was never declared, so every use of it fell back to a
   system font -- which is why the cost and power digits sat off-centre: their
   optical nudge was tuned for Bungee's metrics, not the fallback's. */
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-latin-ext-400.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('vendor/jetbrains-mono/jetbrains-mono-500.woff2') format('woff2');
}
/* Bungee ships in vendor/ but was never declared, so every use of it fell back to a
   system font -- which is why the cost and power digits sat off-centre: their
   optical nudge was tuned for Bungee's metrics, not the fallback's. */
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-latin-ext-400.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('vendor/jetbrains-mono/jetbrains-mono-700.woff2') format('woff2');
}
/* Bungee ships in vendor/ but was never declared, so every use of it fell back to a
   system font -- which is why the cost and power digits sat off-centre: their
   optical nudge was tuned for Bungee's metrics, not the fallback's. */
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-latin-ext-400.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('vendor/jetbrains-mono/jetbrains-mono-800.woff2') format('woff2');
}
/* The tag face, matching JetBrainsMono-ExtraBoldItalic in Keywords.ai */
/* Bungee ships in vendor/ but was never declared, so every use of it fell back to a
   system font -- which is why the cost and power digits sat off-centre: their
   optical nudge was tuned for Bungee's metrics, not the fallback's. */
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bungee';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('vendor/bungee/bungee-latin-ext-400.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: italic;
  font-weight: 800;
  font-display: swap;
  src: url('vendor/jetbrains-mono/jetbrains-mono-800-italic.woff2') format('woff2');
}

:root {
  --font: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  /* paper + the six game colours; --paper is overwritten from the database */
  --paper: #f3f3f4;
  --red: #ef4136;
  --yellow: #fbb040;
  --blue: #27aae1;
  --gray: #939598;
  --green: #39b54a;
  --purple: #662d91;

  --ink-900: #0c0e11;
  --ink-800: #13161b;
  --ink-700: #1a1e24;
  --ink-600: #22272e;
  --ink-500: #2b3138;
  --line: #3a414a;
  --line-hot: #525c6b;

  --text: #e9e5df;
  --text-dim: #98a0a9;
  --text-faint: #6c747d;

  --amber: #f0901b;
  --amber-hi: #ffb43c;
  --amber-dim: #8a5410;

  --chamfer: 11px;
  --glow: 0 0 0 1px rgba(240, 144, 27, 0.4), 0 0 16px -4px rgba(240, 144, 27, 0.5);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
}

body {
  min-height: 100vh;
  background: var(--ink-900);
  color: var(--text);
  font-family: var(--font);
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

i.fa-solid,
i.fa-regular {
  font-family: 'Font Awesome 6 Free';
  font-style: normal;
  display: inline-block;
  line-height: 1;
}
i.fa-solid {
  font-weight: 900;
}
i.fa-regular {
  font-weight: 400;
}

/* --------------------------------------------------------------- backdrops */

.fx-grid,
.fx-scan,
.fx-vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
.fx-grid {
  background-image: linear-gradient(rgba(240, 144, 27, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(240, 144, 27, 0.05) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(115% 85% at 50% 0%, #000 0%, transparent 76%);
  -webkit-mask-image: radial-gradient(115% 85% at 50% 0%, #000 0%, transparent 76%);
}
.fx-scan {
  background: repeating-linear-gradient(to bottom, rgba(255, 255, 255, 0.024) 0 1px, transparent 1px 3px);
}
.fx-vignette {
  background: radial-gradient(110% 80% at 50% 0%, rgba(240, 144, 27, 0.045), transparent 62%),
    radial-gradient(120% 120% at 50% 118%, rgba(0, 0, 0, 0.7), transparent 58%);
}

/* ----------------------------------------------------------- chamfer panels */

.topbar > *,
.toolbar > *,
.cat > *,
.muted {
  color: var(--text-dim);
}

/*
 * The bands of every page: masthead, the page's own bar, then the content.
 *
 * The outline has to be drawn the same way as the shape. An `inset` box-shadow is a
 * rectangle, so clip-path severs it at every chamfer and the diagonals end up with
 * no line at all -- which reads as broken corners. Instead the element is filled
 * with the edge colour and a second, identically clipped layer sits 1px inside it,
 * leaving a 1px ring that follows the chamfer exactly.
 */
.topbar,
.toolbar,
.cat,
.cards-head {
  position: relative;
  isolation: isolate;
  background: var(--line);
  clip-path: polygon(
    var(--chamfer, 14px) 0, calc(100% - var(--chamfer, 14px)) 0,
    100% var(--chamfer, 14px), 100% calc(100% - var(--chamfer, 14px)),
    calc(100% - var(--chamfer, 14px)) 100%, var(--chamfer, 14px) 100%,
    0 calc(100% - var(--chamfer, 14px)), 0 var(--chamfer, 14px)
  );
}
.topbar::before,
.toolbar::before,
.cat::before,
.cards-head::before {
  content: '';
  position: absolute;
  inset: 1px;
  z-index: 0;
  background: var(--ink-700);
  clip-path: polygon(
    calc(var(--chamfer, 14px) - 1px) 0, calc(100% - var(--chamfer, 14px) + 1px) 0,
    100% calc(var(--chamfer, 14px) - 1px), 100% calc(100% - var(--chamfer, 14px) + 1px),
    calc(100% - var(--chamfer, 14px) + 1px) 100%, calc(var(--chamfer, 14px) - 1px) 100%,
    0 calc(100% - var(--chamfer, 14px) + 1px), 0 calc(var(--chamfer, 14px) - 1px)
  );
}
/* the panel's own content has to sit above that layer */
.topbar > *,
.toolbar > *,
.cat > *,
.cards-head > * {
  position: relative;
  z-index: 1;
}

/* the page bar sits one step lighter than the masthead -- on the inner layer, so
   the 1px edge stays the outline colour */
.toolbar::before,
.cards-head::before {
  background: var(--ink-600);
}
.slash {
  color: var(--amber-dim);
  margin: 0 0.5em;
}
code {
  font-family: var(--font);
  font-size: 0.92em;
  color: var(--amber);
}
kbd {
  font-family: var(--font);
  font-size: 0.7rem;
  color: var(--text-faint);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 1px 5px;
}
.hazard {
  display: block;
  height: 5px;
  background: repeating-linear-gradient(-45deg, var(--amber) 0 6px, transparent 6px 12px);
  opacity: 0.5;
}

/* ------------------------------------------------------------------ header */

.topbar {
  margin: 16px clamp(10px, 2.5vw, 28px) 0;
  --chamfer: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 18px;
  flex-wrap: wrap;
}
.brand {
  display: flex;
  align-items: center;
  gap: 13px;
  min-width: 0;
}
.brand-mark {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  font-size: 19px;
  color: #1a1104;
  background: linear-gradient(160deg, var(--amber-hi), var(--amber));
  box-shadow: 0 0 20px -6px rgba(240, 144, 27, 0.9);
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}
.brand h1 {
  margin: 0;
  font-size: clamp(0.95rem, 2.1vw, 1.2rem);
  font-weight: 800;
  letter-spacing: 0.12em;
  color: #fff;
  line-height: 1.15;
}
.brand h1 .plus {
  color: var(--amber);
}
.brand-sub {
  margin: 2px 0 0;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 11px 5px 9px;
  background: var(--ink-900);
  white-space: nowrap;
}
.status .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-faint);
  box-shadow: 0 0 8px currentColor;
}
.status[data-state='saved'] {
  color: var(--green);
  border-color: rgba(57, 181, 74, 0.35);
}
.status[data-state='saved'] .dot {
  background: var(--green);
}
.status[data-state='dirty'] {
  color: var(--amber);
  border-color: rgba(240, 144, 27, 0.4);
}
.status[data-state='dirty'] .dot {
  background: var(--amber);
  animation: pulse 1.4s ease-in-out infinite;
}
.status[data-state='saving'] {
  color: var(--blue);
  border-color: rgba(39, 170, 225, 0.4);
}
.status[data-state='saving'] .dot {
  background: var(--blue);
  animation: pulse 0.6s ease-in-out infinite;
}
.status[data-state='error'] {
  color: var(--red);
  border-color: rgba(239, 65, 54, 0.45);
}
.status[data-state='error'] .dot {
  background: var(--red);
}
.status[data-state='offline'] .dot {
  background: var(--gray);
}
@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.25;
  }
}

/* ----------------------------------------------------------------- buttons */

.btn {
  font-family: var(--font);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  background: var(--ink-600);
  border: 1px solid var(--line-hot);
  border-radius: 3px;
  padding: 7px 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  text-decoration: none;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.btn:hover {
  background: var(--ink-500);
  border-color: var(--amber-dim);
  color: #fff;
}
.btn:focus-visible {
  outline: none;
  box-shadow: var(--glow);
}
.btn-primary {
  background: linear-gradient(170deg, var(--amber-hi), var(--amber));
  border-color: var(--amber-hi);
  color: #1a1104;
}
.btn-primary:hover {
  background: linear-gradient(170deg, #ffc766, var(--amber-hi));
  color: #1a1104;
}
.btn-accent {
  border-color: var(--amber-dim);
  color: var(--amber-hi);
}
.btn-accent:hover {
  background: rgba(240, 144, 27, 0.14);
  color: #fff;
}
.btn i {
  font-size: 0.9em;
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  font-size: 0.8rem;
  color: var(--text-dim);
  background: var(--ink-700);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.icon-btn:hover {
  color: #fff;
  border-color: var(--amber-dim);
  background: var(--ink-600);
}
.icon-btn.danger:hover {
  color: #fff;
  background: var(--red);
  border-color: var(--red);
}

/* ----------------------------------------------------------------- toolbar */

.toolbar {
  margin: 9px clamp(10px, 2.5vw, 28px) 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  flex-wrap: wrap;
}
.field-search {
  flex: 1 1 240px;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 10px;
  background: var(--ink-900);
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--text-faint);
  transition: border-color 0.12s, box-shadow 0.12s;
}
.field-search:focus-within {
  border-color: var(--amber-dim);
  box-shadow: var(--glow);
}
.field-search input {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  outline: none;
  color: var(--text);
  font-family: var(--font);
  font-size: 0.82rem;
}
.field-search input::placeholder {
  color: var(--text-faint);
}
.toolbar-meta {
  margin-left: auto;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* the page's own actions live on the second bar, right-aligned */
.toolbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ------------------------------------------------------------------- board */

.board {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 14px clamp(10px, 2.5vw, 28px) 0;
}
.boot {
  padding: 50px 20px;
  text-align: center;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.cat-head {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 14px;
  background: linear-gradient(90deg, rgba(240, 144, 27, 0.09), transparent 40%);
  border-bottom: 1px solid var(--line);
}
.cat-index {
  font-size: 0.68rem;
  color: var(--amber);
  background: rgba(240, 144, 27, 0.1);
  border: 1px solid rgba(240, 144, 27, 0.25);
  border-radius: 2px;
  padding: 2px 6px;
}
.cat-name {
  font-family: var(--font);
  font-size: 0.86rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #fff;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  padding: 3px 7px;
  min-width: 160px;
  flex: 0 1 auto;
}
.cat-name:hover {
  border-color: var(--line);
}
.cat-name:focus {
  outline: none;
  border-color: var(--amber-dim);
  background: var(--ink-900);
}
.cat-count {
  font-size: 0.68rem;
  color: var(--text-faint);
}
.cat-actions {
  margin-left: auto;
  display: flex;
  gap: 5px;
}

/* ------------------------------------------------------------------- table */

.kw-table {
  overflow-x: auto;
}
.kw-grid {
  display: grid;
  min-width: 1010px;
  grid-template-columns:
    minmax(150px, max-content) /* tag     */
    170px                      /* name    */
    minmax(200px, 1fr)         /* about   */
    180px                      /* symbol  */
    160px                      /* colour  */
    146px;                     /* style   */
  align-items: stretch;
}
.kw-head,
.kw-row {
  display: contents;
}
.cell {
  display: flex;
  align-items: center;
  padding: 8px 13px;
  min-width: 0;
  background: var(--ink-800);
  border-bottom: 1px solid rgba(58, 65, 74, 0.55);
}
.kw-head .cell {
  padding-top: 6px;
  padding-bottom: 6px;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  background: var(--ink-900);
  border-bottom: 1px solid var(--line);
}
.kw-row:hover .cell {
  background: #171b21;
}
.kw-row:last-child .cell {
  border-bottom: 0;
}

/* ---------------------------------------------------------------- the tag */

.tag {
  /* One dial: these multipliers are fractions of the banner height. Keywords.ai
     runs at 0.68 (the maximum); these sit a little above the original 0.45. */
  --tag-h: 28px;
  --tag-fs: calc(var(--tag-h) * 0.54);
  --tag-icon: calc(var(--tag-h) * 0.6);
  --tag-n: 0.43em; /* notch depth: how far the pointed ends cut in */
  --tag-r: 2px; /* corner radius -- every offset below scales with this */
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  height: var(--tag-h);
  padding: 0 0.81em;
  font-family: var(--font);
  font-size: var(--tag-fs);
  line-height: 1;
  font-weight: 800;
  font-style: italic;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--paper);
  color: var(--tag-color, var(--gray));
  /* Rounded hexagon banner. clip-path cannot round a corner, so each of the six
     corners is replaced by three points lying on its arc: the two tangent points
     and the arc midpoint. Every offset is an exact multiple of --tag-r, so
     changing --tag-r re-rounds the shape correctly. --tag-n and --tag-h keep the
     notch and height proportional. */
  clip-path: polygon(
    calc(var(--tag-r) * 0.1956) calc(var(--tag-h) * 0.530085),
    calc(var(--tag-r) * 0.10257) calc(var(--tag-h) * 0.5),
    calc(var(--tag-r) * 0.1956) calc(var(--tag-h) * 0.469915),
    calc(var(--tag-n) - var(--tag-r) * 0.2688) calc(var(--tag-h) * 0.041343),
    calc(var(--tag-n) + var(--tag-r) * 0.10021) calc(var(--tag-h) * 0.011216),
    calc(var(--tag-n) + var(--tag-r) * 0.63817) 0,
    calc(100% - var(--tag-n) - var(--tag-r) * 0.63817) 0,
    calc(100% - var(--tag-n) - var(--tag-r) * 0.10021) calc(var(--tag-h) * 0.011216),
    calc(100% - var(--tag-n) + var(--tag-r) * 0.2688) calc(var(--tag-h) * 0.041343),
    calc(100% - var(--tag-r) * 0.1956) calc(var(--tag-h) * 0.469915),
    calc(100% - var(--tag-r) * 0.10257) calc(var(--tag-h) * 0.5),
    calc(100% - var(--tag-r) * 0.1956) calc(var(--tag-h) * 0.530085),
    calc(100% - var(--tag-n) + var(--tag-r) * 0.2688) calc(var(--tag-h) * 0.958657),
    calc(100% - var(--tag-n) - var(--tag-r) * 0.10021) calc(var(--tag-h) * 0.988784),
    calc(100% - var(--tag-n) - var(--tag-r) * 0.63817) 100%,
    calc(var(--tag-n) + var(--tag-r) * 0.63817) 100%,
    calc(var(--tag-n) + var(--tag-r) * 0.10021) calc(var(--tag-h) * 0.988784),
    calc(var(--tag-n) - var(--tag-r) * 0.2688) calc(var(--tag-h) * 0.958657)
  );
  transition: opacity 0.12s;
}
.tag[data-style='filled'] {
  background: var(--tag-color, var(--gray));
  color: var(--paper);
}
.tag[data-placeholder='true'] {
  opacity: 0.35;
}
.tag i {
  font-size: var(--tag-icon);
  flex: none;
}
/* Our SVG sanitiser strips width/height from your Illustrator files so CSS owns
   the sizing -- without this an inlined asset collapses and disappears. */
.tag svg {
  /* Height is the dial; width follows the artwork's own aspect ratio. Forcing a
     square box letterboxed every non-square icon (xMidYMid meet), which showed up
     as a wide empty gap before the icon and made it look undersized next to the
     near-square Font Awesome glyphs. */
  height: var(--tag-icon);
  width: auto;
  flex: none;
  display: block;
}

/* --------------------------------------------------------------- controls */

.input {
  width: 100%;
  font-family: var(--font);
  font-size: 0.8rem;
  color: var(--text);
  background: var(--ink-900);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 6px 8px;
}
.input:hover {
  border-color: var(--line-hot);
}
.input:focus {
  outline: none;
  border-color: var(--amber-dim);
  box-shadow: var(--glow);
}
.input.name {
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* the description is a sentence, not a label: ordinary weight and case, so it reads
   as prose and is obviously not another name field */
.input.desc {
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-dim);
}
.input.desc:focus {
  color: var(--text);
}
}

.symbol-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 5px 8px;
  background: var(--ink-900);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
  color: var(--text);
  text-align: left;
  font-family: var(--font);
  transition: border-color 0.12s, background 0.12s;
}
.symbol-btn:hover {
  border-color: var(--amber-dim);
  background: var(--ink-700);
}
.symbol-btn:focus-visible {
  outline: none;
  box-shadow: var(--glow);
}
.symbol-glyph {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  font-size: 15px;
  color: var(--text);
  background: var(--ink-600);
  border: 1px solid var(--line);
  border-radius: 3px;
  overflow: hidden;
}
.symbol-glyph svg {
  width: 20px;
  height: 20px;
  display: block;
}
/*
 * The icon's file name is not shown: the glyph is the identifier, and "Asset 72.svg"
 * beside it was reading as content. It stays in the markup for the picker's aria label.
 */
.symbol-name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.swatches {
  display: flex;
  gap: 4px;
}
.swatch {
  width: 21px;
  height: 21px;
  padding: 0;
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, 0.4);
  background: var(--sw, var(--gray));
  cursor: pointer;
  transition: transform 0.1s;
}
.swatch:hover {
  transform: translateY(-1px);
}
.swatch:focus-visible {
  outline: none;
  box-shadow: var(--glow);
}
.swatch[aria-pressed='true'] {
  box-shadow: 0 0 0 2px var(--ink-800), 0 0 0 3px var(--text);
}

.style-toggle {
  font-family: var(--font);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 6px 9px;
  border-radius: 3px;
  cursor: pointer;
  white-space: nowrap;
  border: 1px solid var(--line-hot);
  background: var(--ink-600);
  color: var(--text-dim);
}
.style-toggle[data-style='filled'] {
  border-color: var(--amber-dim);
  color: var(--amber-hi);
  background: rgba(240, 144, 27, 0.12);
}
.style-toggle:hover {
  color: #fff;
  border-color: var(--amber);
}

/* Per-row tools stay invisible until the row is hovered. */
.row-tools {
  margin-left: auto;
  padding-left: 10px;
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.12s;
}
.kw-row:hover .row-tools,
.row-tools:focus-within {
  opacity: 1;
}

.cell-tag::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: var(--tag-color, var(--gray));
  opacity: 0.7;
}
.cell-tag {
  position: relative;
}

.cell-empty {
  color: var(--text-faint);
  font-style: italic;
  grid-column: 1 / -1;
}

/* ----------------------------------------------------------------- preview */

/* ------------------------------------------------------------------ footer */

.footer {
  margin: 16px clamp(10px, 2.5vw, 28px) 24px;
  padding: 0;
  overflow: hidden;
}
.footer p {
  margin: 0;
  padding: 9px 16px;
  font-size: 0.68rem;
  color: var(--text-dim);
}

/* ------------------------------------------------------------------- modal */

.modal-root {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: 22px;
}
.modal-root[hidden] {
  display: none;
}
.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(6, 7, 9, 0.78);
  backdrop-filter: blur(3px);
}
.modal {
  --chamfer: 14px;
  position: relative;
  width: min(820px, 100%);
  max-height: min(84vh, 720px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 40px 80px -30px #000;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(90deg, rgba(240, 144, 27, 0.1), transparent 60%);
}
.modal-head h2 {
  margin: 0;
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--amber);
}
.modal-body {
  padding: 14px;
  overflow: auto;
  flex: 1;
}
.modal-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-top: 1px solid var(--line);
  background: var(--ink-900);
  flex-wrap: wrap;
}
.modal-foot .spacer {
  margin-left: auto;
}
.modal-foot label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.7rem;
  color: var(--text-dim);
  cursor: pointer;
}

.tabs {
  display: flex;
  gap: 5px;
  margin: 12px 0;
}
.tab {
  font-family: var(--font);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: var(--ink-700);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 6px 11px;
  cursor: pointer;
}
.tab:hover {
  color: #fff;
}
.tab[aria-selected='true'] {
  color: #1a1104;
  background: var(--amber);
  border-color: var(--amber-hi);
}

.picker-hint {
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 14px 0 7px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.picker-hint::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}
.picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
  gap: 6px;
}
.picker-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 9px 5px 7px;
  background: var(--ink-700);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
  color: var(--text);
  font-family: var(--font);
  transition: border-color 0.12s, background 0.12s, transform 0.08s;
}
.picker-cell:hover {
  border-color: var(--amber);
  background: var(--ink-600);
  transform: translateY(-1px);
}
.picker-cell[aria-pressed='true'] {
  border-color: var(--amber);
  background: rgba(240, 144, 27, 0.14);
  box-shadow: var(--glow);
}
.picker-cell .glyph {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  font-size: 22px;
  overflow: hidden;
}
.picker-cell .glyph svg {
  width: 26px;
  height: 26px;
  display: block;
}
.picker-cell .label {
  font-size: 0.56rem;
  color: var(--text-dim);
  text-align: center;
  line-height: 1.25;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  word-break: break-word;
  max-width: 100%;
}
.picker-empty {
  padding: 26px 10px;
  text-align: center;
  color: var(--text-faint);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
}
input[type='checkbox'] {
  accent-color: var(--amber);
  width: 14px;
  height: 14px;
  cursor: pointer;
}

/* ------------------------------------------------------------------ toasts */

.toast-root {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 7px;
  align-items: flex-end;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 200px;
  max-width: 360px;
  padding: 10px 13px;
  background: var(--ink-700);
  border: 1px solid var(--line-hot);
  border-left: 3px solid var(--amber);
  border-radius: 3px;
  box-shadow: 0 18px 34px -18px #000;
  font-size: 0.74rem;
  animation: toast-in 0.16s ease-out;
}
.toast.ok {
  border-left-color: var(--green);
}
.toast.err {
  border-left-color: var(--red);
}
.toast button {
  margin-left: auto;
  font-family: var(--font);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  background: none;
  border: 1px solid var(--amber-dim);
  border-radius: 2px;
  padding: 3px 7px;
  cursor: pointer;
}
.toast button:hover {
  background: rgba(240, 144, 27, 0.15);
  color: #fff;
}
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(7px);
  }
}
.noscript {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--ink-900);
  z-index: 99;
}

/* ------------------------------------------------------------- scrollbars */

* {
  scrollbar-width: thin;
  scrollbar-color: var(--ink-500) transparent;
}
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: var(--ink-900);
}
::-webkit-scrollbar-thumb {
  background: var(--ink-500);
  border: 2px solid var(--ink-900);
  border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--amber-dim);
}

/* ------------------------------------------------------------ narrow view */

@media (max-width: 760px) {
  .topbar-right {
    width: 100%;
  }
  .toolbar-meta {
    margin-left: 0;
  }
}

/* The current page stays in the navigation, marked rather than removed, so the four
   destinations are always in the same places. */
.btn.is-active {
  color: var(--ink-900);
  background: var(--amber);
  border-color: var(--amber);
  cursor: default;
}
.btn.is-active:hover {
  background: var(--amber);
}

/*
 * The in-place field.
 *
 * Fixed rather than absolute: a card is scaled with a transform, and a fixed field is
 * measured in real screen pixels, so it lands exactly over the slot whatever the card's
 * scale. It carries no border or background of its own -- the text underneath is what
 * it covers, and it should look like that text became editable.
 */
.inline-edit {
  position: fixed;
  z-index: 60;
  box-sizing: border-box;   /* so height includes the padding the renderer sets */
  margin: 0;
  padding: 0 1px;
  border: 1px solid var(--amber);
  border-radius: 2px;
  background: var(--ink-800, #14161a);
  color: var(--text, #f1f2f2);
  outline: none;
  resize: none;
  overflow: hidden;
  white-space: pre;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.55);
}

/*
 * The holder that positions an in-place field and the keyword menu beneath it.
 * Click-through, so a click anywhere else in the page still reaches its target and
 * blurs the field; the field itself opts back in.
 */
.inline-edit-holder {
  position: fixed;
  z-index: 60;
  pointer-events: none;
}
.inline-edit-holder > .inline-edit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: auto;
  z-index: 1;
}
.inline-edit-holder > .kw-menu {
  left: 0;
  right: auto;
  width: 100%;
  z-index: 2;
  pointer-events: auto;
}

/* ------------------------------------------------- the tag as its own editor */

/*
 * The tag is the record: its colour, style and symbol are already the summary the
 * other columns used to repeat. The name is an input wearing the tag's own type, so it
 * reads as editable text on the banner rather than a form field beside it.
 */

.tag-name::placeholder {
  color: inherit;
  opacity: 0.55;
}
.tag-name:focus {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

/* the glyph is a button, but must not look like one inside the banner */
/*
 * Inside a tag the glyph is bare: the boxed treatment belongs to the symbol button in
 * a form, not to a banner. Its colour is the tag's own -- white on outline, paper on
 * filled -- which is why the markup is rendered tinted.
 */

/*
 * The colour rail: the tag's left edge, which becomes the palette on hover. It is
 * always there -- it is the keyword's colour -- so nothing moves when it opens.
 */

/* a colour chip inside the picker dialog */

/*
 * Picker cells that preview a tag.
 *
 * Choosing a symbol is choosing what the keyword will look like, so the cell shows the
 * banner as it will appear -- colour, style, glyph and name -- instead of a bare glyph
 * with a file name under it.
 */

/*
 * The colour rail.
 *
 * The panel is always 167px wide -- six 24px chips, five 3px gaps, 8px of padding --
 * and sits translated left by all but 4px of that, so what you see closed is a thin
 * bar. Hovering slides it fully into place. One transform, nothing else animating: no
 * width change, no per-chip movement.
 */



/* ------------------------------------------------------------ faction mark

   Shared by the bars, the slot watermarks, the filter chips and the card table's
   faction column. It lives here, not in forge.css, because the card DB does not load
   forge.css -- which is why the table's marks stayed enormous while the bars' were
   correct.
*/

.faction-mark {
  display: grid;
  place-items: center;
  flex: none;
  overflow: hidden;          /* nothing inside may escape the box it was given */
}
.faction-mark svg {
  width: 100%;
  height: 100%;
  display: block;
}
/* the fallback for a symbol with no vendored outline: a glyph sized from the wrapper */
.faction-mark i {
  font-size: 1em;            /* the wrapper carries the size, so 1em is that size */
  line-height: 1;
  display: block;
}

/* =====================================================================
   Rendered keyword text

   Everything that shows an effect -- a card slot, a table cell, a
   faction's bar -- renders through card.js's richText(), which wraps its
   output in .rich-text. These rules are that output's whole contract:
   the tag's height, the <X> mark's size, the vertical alignment of both.

   Containers may override the type size (a card slot is scaled; a table
   cell is dense), and some do. They should not have to override the
   parts that make a tag a tag.
   ===================================================================== */

.rich-text .tag {
  --tag-h: 1.75em;
  vertical-align: middle;
  margin: 0 0.06em;
}

.rich-text .inline-symbol {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0.7em;
  height: 0.8em;
  vertical-align: -0.06em;
  max-height: 1em;
}
.rich-text .inline-symbol svg {
  height: 0.8em;
  width: auto;
  display: block;
}
.rich-text .inline-symbol i {
  font-size: 0.8em;
}

/*
 * The wrapper is an inline box and nothing more.
 *
 * richText() returns a span so every caller shares one styling contract. In a table cell
 * that span must behave like the text it replaced: inline, wrapping with the cell. Given
 * a display of its own it became a block, the cell stopped wrapping its runs of words,
 * and the effect ran under the Faction column.
 */
.rich-text {
  display: inline;
  white-space: inherit;
}
.cards-table td.is-text .rich-text {
  display: inline;
}

/*
 * A number inside an effect: the amount, given the weight the sentence around it does
 * not have. Colour is inherited rather than set, so it works in a card, a table cell and
 * a faction's bar without knowing which it is in.
 */
.rich-num {
  font-weight: 800;
  color: inherit;
}

/* a faction named in an effect: bold, like a number */
.rich-faction {
  font-weight: 800;
  color: inherit;
}


/*
 * The faction's mark, inline before its name.
 *
 * Sized in `em` so it follows the text it sits in -- the same sentence appears on a card,
 * in a table cell and in a faction's bar at three different sizes. The colour is inherited
 * rather than set, so the mark is the colour of the words around it.
 */
.rich-faction-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  width: 1.1em;
  height: 1.1em;
  margin-right: 0.28em;
  color: inherit;
}
.rich-faction-mark .faction-mark {
  width: 100%;
  height: 100%;
  font-size: 1em;
  color: inherit;
}

/*
 * Only the fills factionGlyphPaint replaced are overridden.
 *
 * It writes `fill="url(#gradient)"` onto the root and onto every child that already had a
 * fill, so those are the parts carrying the faction's colour -- and the ones an inline mark
 * must turn into the text colour. A blanket `svg *` also repainted artwork that deliberately
 * has no fill: bat.svg opens with a full-canvas transparent path (`fill: none` from a CSS
 * class), which became a solid black square.
 */
.rich-faction-mark svg[fill^='url('],
.rich-faction-mark svg [fill^='url('] {
  fill: currentColor !important;
}
.rich-faction-mark i {
  /*
   * The glyph fallback -- a symbol with no vendored outline -- is painted by filling the glyph
   * with a gradient and clipping it to the text, which sets `color: transparent`. An inline mark
   * has to undo all three before it can take the colour of the words around it.
   */
  background-image: none !important;
  -webkit-background-clip: border-box !important;
  background-clip: border-box !important;
  -webkit-text-fill-color: inherit !important;
  color: inherit !important;
}

/*
 * A field whose value has a fixed shape.
 *
 * The width is computed in script from the cap and the font, but a measured width can be
 * wrong in ways a stylesheet cannot argue with -- the layout changes under it, a font is
 * not loaded yet. This is the outline: never wider than the characters it accepts.
 */
.inline-edit--capped {
  /* three characters and the caret: no room for a fourth */
  max-width: 4ch;
  min-width: 3ch;
}
