/* ==========================================================================
   The admin dashboard
   ==========================================================================
   A page, not a dialog. Moderation is not a thing you do on top of the map --
   you go looking, you read a list, you compare rows, you come back to it. A
   modal over a globe is the wrong shape for that: it is small, it is temporary,
   and it implies you were in the middle of something else.

   It borrows the app's tokens and its glass, so it is recognisably the same
   product, and none of its layout: a rail, a header, and one wide column.
   ========================================================================== */

/* Every page that is not the map. Safe areas are handled here, once, so a new
   page cannot be added without them: with viewport-fit=cover the window runs
   under the clock and the camera, and a header that does not say so ends up
   behind them. */
.sitepage {
  position: static;
  height: auto;
  min-height: 100dvh;
  overflow: visible;
  background:
    radial-gradient(900px 600px at 12% -10%, color-mix(in srgb, var(--primary) 13%, transparent), transparent 70%),
    var(--bg);
}

/* --- the gate ------------------------------------------------------------- */
.page-gate {
  position: fixed; inset: 0; display: grid; place-items: center;
  padding: env(safe-area-inset-top, 0px) 0 env(safe-area-inset-bottom, 0px);
  background: var(--bg); z-index: 5;
}
.gate-inner { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.gate-inner .mark { width: 44px; height: 44px; }
.gate-inner p { margin: 0; color: var(--ink-2); font-size: 14px; }

/* --- shell ---------------------------------------------------------------- */
.page-shell {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  min-height: 100dvh;
}

.page-rail {
  position: sticky; top: 0; align-self: start;
  height: 100dvh;
  display: flex; flex-direction: column; gap: 6px;
  padding: calc(20px + env(safe-area-inset-top, 0px)) 14px
           calc(20px + env(safe-area-inset-bottom, 0px))
           calc(14px + env(safe-area-inset-left, 0px));
  border-right: 1px solid var(--line);
  background: color-mix(in srgb, var(--panel) 55%, transparent);
  backdrop-filter: blur(18px) saturate(150%);
}
.rail-brand {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 8px 18px; text-decoration: none; color: var(--ink);
}
.rail-brand .mark { width: 30px; height: 30px; }
.rail-word { font-weight: 750; letter-spacing: -.02em; font-size: 17px; }

.rail-nav { display: flex; flex-direction: column; gap: 3px; }
.rail-nav button {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; border: 0; border-radius: var(--r-md);
  background: none; color: var(--ink-2);
  font-size: 14px; font-weight: 600; cursor: pointer; text-align: left;
  transition: background .15s, color .15s;
}
.rail-nav button svg { width: 17px; height: 17px; flex: none; }
.rail-nav button:hover { background: var(--panel-2); color: var(--ink); }
.rail-nav button.on {
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  color: var(--ink);
}

/* The way out is the last row of the menu rather than a chevron floating
   beside it. It is a destination like the others -- it just happens to be a
   different page -- so it looks like one, with a rule above it saying it is
   not another section of this one. */
.rail-out {
  display: flex; align-items: center; gap: 11px;
  margin-top: 6px; padding: 14px 12px 10px;
  border-top: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--ink-2); font-size: 14px; font-weight: 600; text-decoration: none;
  transition: background .15s, color .15s;
}
.rail-out svg {
  width: 17px; height: 17px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.rail-out:hover { background: var(--panel-2); color: var(--ink); }

/* --- header --------------------------------------------------------------- */
.page-main {
  min-width: 0;
  padding: calc(28px + env(safe-area-inset-top, 0px))
           calc(30px + env(safe-area-inset-right, 0px))
           calc(60px + env(safe-area-inset-bottom, 0px))
           30px;
}
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; margin-bottom: 22px;
}
.page-head h1 { margin: 0; font-size: 26px; letter-spacing: -.03em; }
.page-head p { margin: 5px 0 0; color: var(--ink-3); font-size: 13.5px; max-width: 62ch; }
.page-me {
  display: flex; align-items: center; gap: 9px; flex: none;
  padding: 7px 13px 7px 7px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--panel-2);
  font-size: 13px; font-weight: 600;
}
.page-view { display: flex; flex-direction: column; gap: 16px; }

/* --- overview ------------------------------------------------------------- */
/* Four numbers, and the two that mean work sit beside the two that are only
   context -- so "3 suspended" reads as a queue rather than a statistic. */
.dash-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.dash-stat {
  display: flex; flex-direction: column; gap: 3px;
  padding: 18px 20px; border-radius: var(--r-lg);
  border: 1px solid var(--line); background: var(--panel);
}
.dash-stat b { font-size: 34px; letter-spacing: -.035em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.dash-stat span { font-size: 13px; color: var(--ink-2); font-weight: 600; }
.dash-stat em { font-style: normal; font-size: 12px; color: var(--ink-3); }
.dash-stat.needs-work { border-color: color-mix(in srgb, #F43F5E 42%, transparent); }
.dash-stat.needs-work b { color: #FB7185; }

.dash-note {
  margin: 0; padding: 16px 18px; border-radius: var(--r-lg);
  border: 1px solid var(--line); background: var(--panel-2);
  color: var(--ink-2); font-size: 13.5px; line-height: 1.6; max-width: 70ch;
}
.dash-note b { color: var(--ink); }

/* --- lists ---------------------------------------------------------------- */
.dash-tools { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
/* .segmented.compact is a fixed 148px, which is right for the two-word theme
   toggle it was written for and truncates anything else. These size to their
   labels. */
#dashFilters.segmented.compact { width: auto; flex: 0 0 auto; }
#dashFilters button { flex: 0 0 auto; padding: 0 14px; height: 30px; }
.dash-tools .asearch { max-width: 340px; height: 38px; }
.dash-list {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--panel); overflow: hidden;
}
.dash-list .arow {
  border: 0; border-radius: 0; background: none;
  border-bottom: 1px solid var(--line);
  padding: 12px 16px;
}
.dash-list .arow:last-child { border-bottom: 0; }
.dash-list .arow:hover { background: var(--panel-2); }
.dash-list .arow.is-banned { border-left: 3px solid #F43F5E; }
.dash-empty { padding: 44px 20px; text-align: center; color: var(--ink-3); font-size: 14px; }

/* --- tags ----------------------------------------------------------------- */
/* The disc at the head of a tag row is the whole feature in one square: an
   emoji if it has one, otherwise the most-liked photograph filed under it,
   otherwise a letter. All three are the same size and shape, so the table
   reads as one column and the difference between them is the picture rather
   than the layout. */
.tagbar {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  justify-content: space-between;
  padding: 0 0 12px;
}
.tagbar-counts { font-size: 13px; color: var(--ink-3); }
.tagbar-counts b { color: var(--ink); font-variant-numeric: tabular-nums; }
.tagbar-do { display: flex; gap: 8px; flex: none; }

.tagface {
  width: 38px; height: 38px; flex: none;
  border-radius: 11px;
  display: grid; place-items: center; overflow: hidden;
  border: 1px solid var(--line);
  background: var(--panel-2);
}
.tagface.is-emoji { font-size: 19px; line-height: 1; }
.tagface.is-photo { padding: 0; border-color: transparent; }
.tagface.is-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The lettered disc, in the tag's own hue -- the same construction the app
   uses, so a tag with no face looks the same in both places. */
.tagface.is-letter {
  color: #fff; font-weight: 700; font-size: 16px;
  background: linear-gradient(140deg, hsl(var(--h) 70% 56%), hsl(calc(var(--h) + 40) 70% 44%));
  border-color: transparent;
}
/* A tag nobody has given a face to is the thing this table is for finding, so
   it is marked rather than left to be spotted. */
.dash-list .tagrow:has(.is-letter) { border-left: 3px solid var(--ink-3); }

@media (max-width: 720px) {
  /* The buttons go under the name rather than beside it, which is what the
     rest of this table already does at this width. */
  .tagbar { flex-direction: column; align-items: stretch; }
  .tagbar-do { justify-content: flex-end; }
}
.dash-more { display: flex; justify-content: center; padding: 6px 0 0; }

/* --- the hamburger, on a phone ------------------------------------------- */
/* Hidden on a desktop, where the rail is always there and always readable. */
.rail-burger { display: none; }
.rail-scrim { display: none; }

@media (max-width: 860px) {
  /* Rows, not just one column. The shell is min-height:100dvh and grid rows
     stretch to fill by default, so with the rail no longer a full-height
     column its row was stretched to a third of the screen and the bar sat at
     the top of 248px of nothing. The bar takes what it needs; the page takes
     the rest. */
  .page-shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }

  /* The rail becomes a bar with the section's name on it and a button that
     opens the rest.
     It used to be the same list of buttons laid out sideways and scrolled. On
     four items that fitted; on six it does not, and a nav you have to swipe to
     discover is a nav whose last item nobody finds. The name of where you are
     is also worth more than three icons you cannot fully see. */
  .page-rail {
    position: sticky; top: 0; z-index: 30;
    height: auto; flex-direction: row; align-items: center; gap: 10px;
    /* The top of the page on a phone, so it is what has to clear the clock. */
    padding: calc(9px + env(safe-area-inset-top, 0px))
             calc(12px + env(safe-area-inset-right, 0px)) 9px
             calc(12px + env(safe-area-inset-left, 0px));
    border-right: 0; border-bottom: 1px solid var(--line);
    overflow: visible;
  }
  .rail-brand { padding: 0; }
  .rail-word { display: none; }
  /* It lives in the menu now rather than in the bar, so the bar is the mark,
     the section's name and the button that opens the rest -- nothing else. */
  .rail-out { font-size: 15px; padding: 13px 12px 11px; }

  /* Where you are, between the mark and the button that changes it. */
  .rail-here {
    order: 2; flex: 1; min-width: 0;
    font-size: 15px; font-weight: 700; letter-spacing: -.01em;
    color: var(--ink);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .rail-burger {
    order: 4;
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; flex: none;
    border: 1px solid var(--line); border-radius: var(--r-md);
    background: var(--panel-2); color: var(--ink); cursor: pointer;
    transition: background .15s, border-color .15s;
  }
  .rail-burger:active { background: var(--panel); }
  .rail-burger svg { width: 18px; height: 18px; }
  .rail-burger[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }

  /* The list itself, dropped from under the bar. Positioned against the rail
     rather than the page so it stays put while the page behind it scrolls. */
  .rail-nav {
    position: absolute;
    top: 100%; left: 8px; right: 8px;
    flex-direction: column; gap: 2px;
    padding: 8px;
    border: 1px solid var(--line); border-radius: var(--r-lg);
    /* Two layers, because --panel is translucent by design -- it is meant to
       sit on the page and let it through. A menu may not: it lies over the
       thing you are reading, and reading the section names through the heading
       behind them is not a menu. The opaque ground goes underneath and the
       panel keeps its own tint on top. */
    background-color: var(--bg);
    background-image: linear-gradient(var(--panel), var(--panel));
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    box-shadow: 0 18px 44px rgba(0,0,0,.5);
    transform-origin: top center;
    z-index: 31;
  }
  .rail-nav[hidden] { display: none; }
  .rail-nav button { padding: 11px 12px; white-space: nowrap; font-size: 15px; }

  /* A tap anywhere else closes it, and it has to be under the menu and over
     everything else for that to be true. */
  .rail-scrim {
    display: block; position: fixed; inset: 0; z-index: 29;
    background: transparent;
  }
  .rail-scrim[hidden] { display: none; }

  @media (prefers-reduced-motion: no-preference) {
    .rail-nav:not([hidden]) { animation: railDrop .16s var(--ease-soft); }
  }
  @keyframes railDrop {
    from { opacity: 0; transform: translateY(-6px) scale(.98); }
    to   { opacity: 1; transform: none; }
  }
  .page-main {
    padding: 18px calc(14px + env(safe-area-inset-right, 0px))
             calc(40px + env(safe-area-inset-bottom, 0px))
             calc(14px + env(safe-area-inset-left, 0px));
  }
  .page-head { flex-direction: column; gap: 12px; }
  .page-head h1 { font-size: 22px; }
  .dash-stat { padding: 15px 16px; }
  .dash-stat b { font-size: 28px; }
  /* The actions drop under the name; four columns do not fit a phone. */
  .dash-list .arow { grid-template-columns: auto minmax(0, 1fr); }
  .dash-list .arow-tags, .dash-list .arow-do { grid-column: 2; }
  .dash-list .arow-do { justify-content: flex-start; }
}

/* --- settings ------------------------------------------------------------- */
/* Four short sections, so they are laid out as one column of cards you scroll
   rather than tabs that hide three to show one. The rail scrolls to them and
   follows the scroll back. */
.setcols { gap: 16px; max-width: 780px; }
.setcols .sset.card {
  gap: 14px;
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--panel);
  scroll-margin-top: 20px;
}
.setcols .sset h3 {
  margin: 0;
  font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3);
}
.setcols .setrow { gap: 18px; }
.setcols .setrow + .setrow { padding-top: 14px; border-top: 1px solid var(--line); }
.setcols .btn.wide { justify-content: center; }
/* The theme picker.
 *
 * A row where the label sits left and the control sits right works for two
 * options and not for seven, so this one stacks: the description above, the
 * grid below, using the whole width of the card. Everything else on the page
 * keeps the shape it had.
 *
 * Each button carries its own three colours as --s1/--s2/--s3, set inline from
 * the theme's own palette. The swatch is painted from those rather than from a
 * copy of the hexes here, so a palette that changes in themes.css changes the
 * picker with it and there is no second list to keep in step. */
.setrow.setrow-wide { flex-direction: column; align-items: stretch; gap: 12px; }

.themegrid {
  display: grid;
  /* Its own width, not the one its parent's align-items would give it. The
     phone block further down this file sets align-items: flex-start on every
     setrow, which would size this to its content and collapse the grid to a
     single column -- and it wins, because it comes later at the same weight.
     Saying the width here means the ordering cannot decide it. */
  width: 100%;
  /* auto-fill rather than a fixed count: the card is a different width on a
     phone, in the settings column and on a wide screen, and the grid should
     answer that rather than three media queries. */
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}
.themegrid button {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 10px 12px 11px;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  transition: border-color .16s, box-shadow .16s, transform .16s var(--ease);
}
.themegrid button:hover { border-color: var(--line-2); }
.themegrid button:active { transform: scale(.985); }
.themegrid button.on {
  border-color: transparent;
  box-shadow: var(--glow);
}

/* Three bands of the theme, not three dots: a dot shows a hue and a band shows
   how the hues sit next to each other, which is what a theme actually is. The
   third is the ground, and it is the widest because that is what most of the
   screen will be. */
.themeswatch {
  width: 100%; height: 26px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line);
  margin-bottom: 4px;
  background: linear-gradient(90deg,
    var(--s1) 0 26%, var(--s2) 26% 46%, var(--s3) 46% 100%);
}
.themename { font-size: 13px; font-weight: 700; line-height: 1.2; }
.themenote { font-size: 11.5px; color: var(--ink-3); line-height: 1.25; }

/* Plans.
 *
 * Deliberately not a pricing table with a highlighted "most popular" column.
 * There are two paid things and they are for different subjects -- one is for
 * an account and one is for a group -- so they are two cards rather than tiers
 * of the same thing, and nothing here nudges toward either. */
.plangrid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px; margin-top: 4px;
}
.plancard {
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 18px; background: var(--panel-2);
  display: flex; flex-direction: column;
}
.plancard.on { border-color: var(--primary); box-shadow: var(--glow); }
.plancard h4 { margin: 0; font-size: 16px; font-weight: 700; }
.planprice {
  font-size: 27px; font-weight: 700; letter-spacing: -.02em;
  margin: 6px 0 2px; font-variant-numeric: tabular-nums;
}
.planprice em {
  font-style: normal; font-size: 12px; font-weight: 500;
  color: var(--ink-3); letter-spacing: 0; margin-left: 4px;
}
.planblurb { margin: 2px 0 10px; font-size: 13.5px; color: var(--ink-2); }
.plancard ul {
  margin: 0 0 14px; padding-left: 18px; font-size: 13.5px;
  color: var(--ink-2); flex: 1;
}
.plancard li { margin-bottom: 5px; }
/* A button that cannot do anything says so by looking like it. */
.plancard .btn[disabled] { opacity: .5; cursor: not-allowed; }

/* Sign out is not a setting, so it sits under a rule rather than in the run of
   them, and it is the only red on the page. */
.setcols .setrow.signout { padding-top: 16px; border-top: 1px solid var(--line); }

@media (max-width: 860px) {
  .setcols { max-width: none; }
  .setcols .sset.card { padding: 16px; }
  .setcols .setrow { flex-direction: column; align-items: flex-start; gap: 10px; }
  /* Full width, and the two options split it: a toggle where the labels sit in
     the left third and the rest is empty does not look like a pair of choices. */
  .setcols .segmented.compact { width: 100%; }
  .setcols .segmented.compact button { flex: 1; height: 34px; }
}

/* --- credits -------------------------------------------------------------- */
/* A rail entry that is a link out, and one that is where you already are. */
.raillink, .railhere {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; border-radius: var(--r-md);
  font-size: 14px; font-weight: 600; text-decoration: none;
}
.raillink svg, .railhere svg { width: 17px; height: 17px; flex: none; }
.raillink { color: var(--ink-2); }
.raillink:hover { background: var(--panel-2); color: var(--ink); }
.railhere { background: color-mix(in srgb, var(--primary) 18%, transparent); color: var(--ink); }

.credits { margin: 0; display: flex; flex-direction: column; gap: 16px; }
.credits dt {
  font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
}
.credits dd { margin: 4px 0 0; font-size: 14px; color: var(--ink-2); }
.credits dd b { color: var(--ink); font-weight: 650; }
.credits dd span { display: block; margin-top: 3px; font-size: 12.5px; color: var(--ink-3); }
.credits a { color: var(--primary); text-decoration: none; }
.credits a:hover { text-decoration: underline; }

/* --- bio and banner ------------------------------------------------------- */
/* The count sits under the box rather than beside the label, so it is next to
   the thing it is counting and not next to the thing it is describing. */
.field .fieldnote {
  align-self: flex-end;
  font-size: 11px; font-weight: 500;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

.field textarea {
  resize: vertical;
  min-height: 62px;
  font: inherit;
  line-height: 1.45;
}

/* The preview is the control. A file input's own button says "Choose file" and
   then a filename, neither of which is the question -- the question is whether
   this picture looks right behind your name, and the only answer to that is the
   picture. Sized to the shape it will be on the profile. */
.bannerpick {
  display: block;
  width: 100%;
  aspect-ratio: 1000 / 260;
  padding: 0;
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px dashed var(--line);
  background: var(--panel-3);
  color: var(--ink-3);
  font: inherit; font-size: 12.5px; font-weight: 600;
  cursor: pointer;
  transition: border-color .16s, color .16s;
}
.bannerpick:hover { border-color: var(--ink-3); color: var(--ink-2); }
.bannerpick img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Solid once there is something in it: the dashes mean "empty, put something
   here", and they stop meaning that the moment something is there. */
.bannerpick:has(img:not([hidden])) { border-style: solid; border-color: var(--line); }

/* --- the avatar, as a control --------------------------------------------- */
/* The picture, the initial and the emoji are one setting with three possible
   answers, so they share one control rather than sitting in three rows. */
.avatarpick {
  position: relative; flex: none;
  width: 72px; height: 72px; padding: 0;
  border: 0; border-radius: 50%; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.avatarpick .avatar.big { width: 72px; height: 72px; font-size: 27px; }
.avatarpick img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; border-radius: 50%;
  object-fit: cover;
}
.avatarpick img[hidden] { display: none; }
.avatarpick-badge {
  position: absolute; right: -2px; bottom: -2px;
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--panel); border: 1px solid var(--line);
  color: var(--ink-2);
  transition: color .15s, border-color .15s;
}
.avatarpick-badge svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.avatarpick:hover .avatarpick-badge { color: var(--accent); border-color: var(--accent); }
.avatarpick:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* --- sections as pages ---------------------------------------------------- */
/* One at a time, rather than one column of everything.
   Four short cards were a reasonable scroll; six are not, and on a phone the
   thing you came to change was usually below the fold of something you did
   not. The rail picks one and the rest are not in the document flow at all --
   `hidden`, so nothing in them is tabbable while it is off-screen. */
.setcols .sset.card[hidden] { display: none; }
.setnote {
  margin: 0; color: var(--ink-2); font-size: 13.5px; line-height: 1.55;
  padding-bottom: 2px;
}

/* --- the block list ------------------------------------------------------- */
.blocklist { display: flex; flex-direction: column; gap: 6px; }
.blockrow {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--panel-2);
}
.blockrow .card-av { width: 30px; height: 30px; font-size: 13px; flex: none; }
.blockrow b { font-size: 14px; font-weight: 650; }
.blockrow span { color: var(--ink-3); font-size: 12.5px; }
.blockrow .grow { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.blockrow button { flex: none; }

/* --- the dashboard's charts ----------------------------------------------- */
.dash-charts { display: flex; flex-direction: column; gap: 14px; margin: 16px 0 20px; }
.dash-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.dash-card {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--panel); padding: 16px 18px 14px;
  display: flex; flex-direction: column; gap: 10px; min-width: 0;
}
.dash-card header { display: flex; flex-direction: column; gap: 2px; }
.dash-card h3 { margin: 0; font-size: 14px; font-weight: 700; letter-spacing: -.01em; }
.dash-card header p { margin: 0; font-size: 12.5px; color: var(--ink-3); }

/* The window picker sits with the charts it changes, not in the page header,
   where it would look as though it filtered the totals above as well. */
.chart-head { display: flex; gap: 4px; }
.chart-head button {
  padding: 4px 10px; border: 1px solid var(--line); border-radius: var(--r-pill);
  background: none; color: var(--ink-3); font-size: 12px; font-weight: 650; cursor: pointer;
}
.chart-head button.on { color: var(--ink); border-color: var(--primary); background: var(--panel-2); }

.chart { display: block; width: 100%; height: auto; overflow: visible; }
.chart .g { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.chart .area { fill: color-mix(in srgb, var(--primary) 20%, transparent); stroke: none; }
.chart .line {
  fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.chart .dot { fill: var(--primary); stroke: var(--panel); stroke-width: 2; }
.chart-axis {
  display: flex; justify-content: space-between;
  font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums;
}

/* A ranked list, where the bar is the row rather than a separate column. */
.rank { display: flex; flex-direction: column; gap: 7px; }
.rank-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 90px auto;
  align-items: center; gap: 10px; font-size: 13px;
}
.rank-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-2); }
.rank-bar { height: 7px; border-radius: 100px; background: var(--panel-2); overflow: hidden; }
.rank-bar i {
  display: block; height: 100%; border-radius: 100px;
  background: linear-gradient(90deg, var(--primary), color-mix(in srgb, var(--primary) 55%, transparent));
}
.rank-n { font-variant-numeric: tabular-nums; color: var(--ink-3); font-size: 12.5px; min-width: 30px; text-align: right; }

@media (max-width: 860px) {
  /* Two charts side by side is two charts nobody can read. */
  .dash-pair { grid-template-columns: minmax(0, 1fr); }
  .dash-card { padding: 14px 15px 12px; }
  .rank-row { grid-template-columns: minmax(0, 1fr) 64px auto; }
}

/* --- sources -------------------------------------------------------------- */
/* The switch reads as its own state rather than as a control: whether this is
   running is the first thing anyone opening the page wants to know. */
.switchbar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 14px 18px; margin-bottom: 14px;
  border: 1px solid var(--line); border-left-width: 4px; border-radius: var(--r-lg);
  background: var(--panel);
}
.switchbar.on { border-left-color: #34D399; }
.switchbar.off { border-left-color: var(--ink-3); }
.switchbar > div:first-child { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 2px; }
.switchbar b { font-size: 15px; }
.switchbar span { font-size: 13px; color: var(--ink-3); }
.switchbar-do { display: flex; gap: 8px; }

.feedadd { display: grid; grid-template-columns: 1.4fr 2fr .7fr .7fr auto; gap: 8px; }
.feedadd input {
  min-width: 0; padding: 9px 11px; font-size: 14px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--panel-2); color: var(--ink);
}
.feedadd input::placeholder { color: var(--ink-3); }

.feedlist { display: flex; flex-direction: column; gap: 8px; margin: 14px 0 20px; }
.feedrow {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px;
  border: 1px solid var(--line); border-left-width: 3px; border-radius: var(--r-lg);
  background: var(--panel);
}
.feedrow.ok { border-left-color: #34D399; }
.feedrow.bad { border-left-color: #F43F5E; }
.feedrow.off { border-left-color: var(--ink-3); opacity: .72; }
.feedrow.idle { border-left-color: var(--line); }
.feedrow-main { flex: 1; min-width: 220px; display: flex; flex-direction: column; gap: 2px; }
.feedrow-main b { font-size: 14.5px; }
.feedrow-url, .feedrow-meta {
  font-size: 12px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.feedrow-state { font-size: 12px; color: var(--ink-2); }
.feedrow.bad .feedrow-state { color: #F43F5E; }
.feedrow-do { display: flex; gap: 6px; }

@media (max-width: 860px) {
  .feedadd { grid-template-columns: 1fr 1fr; }
  .feedadd input:nth-child(2) { grid-column: 1 / -1; }
  .feedadd button { grid-column: 1 / -1; }
  .feedrow-do { width: 100%; }
  .feedrow-do .btn { flex: 1; }
}

/* --- reviewing the moderators ---------------------------------------------- */
.modlist { display: flex; flex-direction: column; gap: 10px; margin: 14px 0 20px; }
.modrow {
  padding: 13px 15px;
  border: 1px solid var(--line); border-left-width: 3px;
  border-radius: var(--r-lg); background: var(--panel);
  display: flex; flex-direction: column; gap: 7px;
}
.modrow.bad { border-left-color: #F43F5E; }
.modrow.ok { border-left-color: #34D399; }
.modrow.idle { border-left-color: var(--ink-3); }
/* Read ones recede rather than disappear: the record is the point, and a list
   that hides what has been looked at is a list you cannot check twice. */
.modrow.seen { opacity: .62; }

.modrow-head {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 14px;
}
.modrow-head b { font-weight: 650; }
.modrow-target { color: var(--primary); font-weight: 600; }
/* What was acted on, beside who wrote it. Quieter than the account, because
   for a removal the account is the thing a reader scans for and the subject is
   what they check once they have found the row. */
.modrow-thing { color: var(--ink-3); font-size: 12.5px; font-family: var(--mono); }
.modrow-who { margin-left: auto; color: var(--ink-3); font-size: 12.5px; display: flex; gap: 6px; align-items: center; }
.modrow-when { color: var(--ink-3); font-size: 12.5px; }

/* What they were acting on, so a decision can be judged without opening it. */
.modrow-quote {
  margin: 0; padding: 8px 11px;
  border-left: 2px solid var(--line); border-radius: 0 var(--r-md) var(--r-md) 0;
  background: var(--panel-2);
  color: var(--ink-2); font-size: 13px; font-style: italic;
}
.modrow-quote em { font-style: normal; color: var(--ink-3); font-size: 12px; }

/* The sentence they wrote. It is the reason this table exists, so it is the
   thing set in the readable size rather than in the small print. */
.modrow-reason { margin: 0; font-size: 14px; color: var(--ink); line-height: 1.5; }
.modrow-reason.none { color: var(--ink-3); font-style: italic; font-size: 13px; }
.modrow-evidence {
  margin: 0; font-size: 12.5px; color: var(--ink-2);
  word-break: break-word;
}
.modrow-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.modrow-seen { color: var(--ink-3); font-size: 12.5px; }

/* How many people have said the same thing about one subject. Set apart from
   the rest of the head because it is the single most useful number on the
   row: five people objecting to one Bubl is a different fact from one person
   objecting five times. */
.repcount {
  font-size: 11.5px; font-weight: 700; padding: 1px 7px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, #F43F5E 16%, transparent);
  color: #FB7185;
}
/* The kind filter sits beside the waiting/decided one and is as long as there
   are kinds waiting, so it wraps rather than squeezing. */
#repKinds.segmented.compact { width: auto; flex: 0 0 auto; }

@media (max-width: 860px) {
  .modrow-who { margin-left: 0; width: 100%; }
  .modrow-foot .btn { flex: 1; text-align: center; }
}

/* ==========================================================================
   Daily Bubls -- the settings for what the site writes for itself
   ==========================================================================
   Four fields and nothing clever. Every one carries a line of small print
   saying what the number does, because an operator setting a ceiling on how
   many invented Bubls may sit on a live map should not have to guess what
   "stop at" means. */
.apform {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px 16px; margin-top: 12px;
}
.apfield { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.apfield.wide { grid-column: 1 / -1; }
.apfield > span { font-size: 13px; font-weight: 600; }
.apfield input {
  min-width: 0; padding: 9px 11px; font-size: 14px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--panel-2); color: var(--ink);
}
.apfield input::placeholder { color: var(--ink-3); }
.apfield small { font-size: 11.5px; line-height: 1.45; color: var(--ink-3); }

.apdo { grid-column: 1 / -1; display: flex; gap: 8px; }
.apdo.spread { margin-top: 10px; flex-wrap: wrap; }

/* The two commands that refill the bank. A <pre> rather than prose: they are
   meant to be selected and pasted, and prose that wraps is not. */
.apcode {
  margin: 10px 0 0; padding: 11px 13px; overflow-x: auto;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--panel-2); color: var(--ink-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.6;
}
