/* ============================================
   HONEYPOT LEADERBOARD (BOXSCORE) - defaultcreds.lol
   Loaded after styles.css. Every rule is scoped under body.page-honeypot and
   every component class is prefixed hp-. Palette: tokens from styles.css plus
   the orange family below. Green, grey, orange and one red; no radii anywhere.
   ============================================ */

body.page-honeypot {
  --hp-orange: #f97316;
  --hp-orange-text: #fb923c;
  --hp-orange-soft: rgba(249, 115, 22, 0.15);
  --hp-bar: rgba(34, 197, 94, 0.55);
  /* The underline under a link in running text: green, quieter than the link itself. */
  --hp-link-underline: rgba(34, 197, 94, 0.45);
  --hp-rank-1: var(--color-accent);
  --hp-rank-2: var(--color-text);
  --hp-rank-3: var(--color-text-secondary);
  --hp-row: 36px;
  --hp-tail-row: 32px;
  --hp-mini-row: 28px;
  --hp-max: 1280px;
  --hp-gutter: 24px;
  --hp-bar-w: 120px;
  --hp-histo-h: 160px;
}

/* --- Square corners, everywhere on these pages ---
   No radius and no rounded SVG rect, whatever a component or a browser default would
   draw. The shared header squares its own corners on every surface, the padlocks creds.js
   draws with rx="2" included (partials/header.css); these rules hold the rest of the page
   to the same. */
body.page-honeypot *,
body.page-honeypot *::before,
body.page-honeypot *::after {
  border-radius: 0 !important;
}

body.page-honeypot svg rect {
  rx: 0;
  ry: 0;
}

/* Every hp- component sets its own display, which would otherwise beat the
   UA's [hidden] { display: none } on specificity. */
body.page-honeypot [hidden] {
  display: none !important;
}

/* The header is not styled here any more. partials/header.css owns it on every
   defaultcreds.lol surface, including the things this file used to hold on its own: the
   width (which is why the header was narrower on these two pages than on the other
   five), the orange password, the square corners under the open dropdown and the
   refresh spin. The one rule left below is about this page's layout, not the header's
   appearance, and honeypot/tests/test_header.py names it as the single exception. */

@media (max-width: 768px) {
  /* Not appearance: the board is a long scroll of panels and a header that follows it
     down a phone screen costs a row of them. partials/header.css keeps the header
     sticky everywhere else. */
  body.page-honeypot .site-header {
    position: static;
  }
}

/* --- Skip link --- */
body.page-honeypot .hp-skip {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 200;
  /* Standalone control, so the 40px target rule (spec 9) applies; 8px
     padding on a 13px line only reached 29px. */
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 8px 12px;
  background: var(--color-accent);
  color: var(--color-bg);
  font: 700 13px/1 var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

body.page-honeypot .hp-skip:focus {
  left: 8px;
  outline: 2px solid var(--color-accent-hover);
  outline-offset: 2px;
}

/* --- Page grid --- */
body.page-honeypot .hp-page {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--hp-gutter);
  row-gap: 32px;
  max-width: var(--hp-max);
  margin: 0 auto;
  padding: 24px var(--hp-gutter) 48px;
}

body.page-honeypot .hp-page > * {
  grid-column: 1 / -1;
  min-width: 0;
}

/* The header is sticky and 64px tall, so an un-offset anchor drops the
   visitor under it: "full list" landed with the PLAY-BY-PLAY heading and its
   honesty line entirely behind the chrome, and the skip link put keyboard
   users in the same place. */
body.page-honeypot #board,
body.page-honeypot .hp-page section[id] {
  scroll-margin-top: 80px;
}

body.page-honeypot .hp-section.is-ready {
  animation: hp-fade 200ms ease-out 1;
}

@keyframes hp-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* --- Section header --- */
body.page-honeypot .hp-section__head {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px 24px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-border);
}

body.page-honeypot .hp-section__titles {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 16px;
  min-width: 0;
}

body.page-honeypot .hp-section__head h2 {
  font: 800 28px/1.1 var(--font-sans);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-text);
}

body.page-honeypot .hp-section__sub {
  font: 400 12px/1.4 var(--font-mono);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-section__ctl {
  margin-left: auto;
}

body.page-honeypot .hp-section__cap {
  margin-top: 12px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-section__body {
  margin-top: 16px;
}

/* --- Hero --- */
body.page-honeypot .hp-hero {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--hp-gutter);
  row-gap: 16px;
}

body.page-honeypot .hp-hero__main {
  grid-column: span 8;
  min-width: 0;
}

body.page-honeypot .hp-hero__side {
  grid-column: span 4;
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 395px;
  min-width: 0;
}

body.page-honeypot .hp-eyebrow {
  font: 700 12px/1 var(--font-sans);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent);
}

body.page-honeypot .hp-hero__num {
  margin: 4px 0 8px;
  min-height: 128px;
  font: 700 128px/1 var(--font-mono);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  white-space: nowrap;
}

body.page-honeypot .hp-hero__num.is-len-8 { font-size: 96px; }
body.page-honeypot .hp-hero__num.is-len-10 { font-size: 72px; }

body.page-honeypot .hp-hero__num.is-loading,
body.page-honeypot .hp-hero__num.is-offline {
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-hero__cap {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  font: 500 18px/1.4 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-hero__disclosure {
  margin-top: 16px;
  max-width: 62ch;
  font: 400 14px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-hero__disclosure strong {
  font-weight: 600;
  color: var(--color-text);
}

/* --- Chips + badges --- */
body.page-honeypot .hp-chip {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 6px;
  border: 1px solid var(--color-border-strong);
  font: 700 10px/1 var(--font-sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  white-space: nowrap;
  flex-shrink: 0;
}

body.page-honeypot .hp-chip--accent {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

body.page-honeypot .hp-chip--orange {
  color: var(--hp-orange-text);
  border-color: var(--hp-orange);
}

body.page-honeypot .hp-chip--danger {
  color: var(--color-error);
  border-color: var(--color-error);
}

/* js/sessions.js sets this on the chips that carry no warning (an SSH session, a session
   that never got in). It had no rule, so those chips looked neutral only because the base
   chip does; written out, a later change to the base cannot recolour them by accident. */
body.page-honeypot .hp-chip--neutral {
  color: var(--color-text-secondary);
  border-color: var(--color-border-strong);
}

body.page-honeypot .hp-chip--mono {
  font: 400 12px/1 var(--font-mono);
  letter-spacing: 0;
}

body.page-honeypot .hp-cc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 18px;
  min-width: 28px;
  padding: 0 4px;
  border: 1px solid var(--color-border-strong);
  font: 400 12px/1 var(--font-mono);
  color: var(--color-text);
  flex-shrink: 0;
}

body.page-honeypot .hp-cc.is-unknown {
  color: var(--color-text-secondary);
}

/* --- Buttons --- */
body.page-honeypot .hp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--color-border-strong);
  font: 700 12px/1 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  background: transparent;
  cursor: pointer;
}

body.page-honeypot .hp-btn:hover {
  color: var(--color-text);
  background: var(--color-bg-hover);
}

body.page-honeypot .hp-btn:focus-visible,
body.page-honeypot .hp-more:focus-visible,
body.page-honeypot .hp-val:focus-visible,
body.page-honeypot .hp-mini__link:focus-visible,
body.page-honeypot .hp-footer a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.page-honeypot .hp-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  height: 40px;
  border-top: 1px solid var(--color-border);
  font: 700 12px/1 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  cursor: pointer;
}

body.page-honeypot .hp-more::after {
  content: "\2304";
  display: inline-block;
  font-size: 14px;
  line-height: 1;
  transition: transform var(--transition-fast);
}

body.page-honeypot .hp-more[aria-expanded="true"]::after {
  transform: rotate(180deg);
}

body.page-honeypot .hp-more:hover {
  color: var(--color-text);
}

/* --- Status panel --- */
body.page-honeypot .hp-status {
  padding: 12px 16px;
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  font: 400 12px/1.5 var(--font-mono);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-status__head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
}

body.page-honeypot .hp-status__dot {
  width: 8px;
  height: 8px;
  background: var(--color-text-secondary);
  flex-shrink: 0;
}

body.page-honeypot .hp-status__word {
  font-weight: 700;
  color: var(--color-text);
  letter-spacing: 0.04em;
}

body.page-honeypot .hp-status__btn {
  margin-left: auto;
}

body.page-honeypot .hp-status__meta,
body.page-honeypot .hp-status__delta,
body.page-honeypot .hp-status__live,
body.page-honeypot .hp-status__prov {
  margin-top: 4px;
}

body.page-honeypot .hp-status__delta,
body.page-honeypot .hp-status__live {
  color: var(--color-text);
}

body.page-honeypot .hp-status__delta-n::before {
  color: var(--color-text-secondary);
}

/* Time to first hit. Labels carry the panel's secondary colour without using
   .hp-status__lbl, which is display:none at <= 1024 px where these labels are
   the only thing naming the numbers. */
body.page-honeypot .hp-status__live-lbl,
body.page-honeypot .hp-status__live-sep {
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-status__live-best {
  color: var(--hp-orange-text);
}

/* Each clause wraps as a unit. Without this the line breaks mid-phrase at the
   widths where it needs two rows: "fastest yet" left "yet" alone on the second
   row in orange, which reads as a rendering fault, and "FIRST TOUCH" split the
   same way. Combined with the non-breaking space in liveSep(), the only break
   points left are the ones after a middot. */
body.page-honeypot .hp-status__live-lbl,
body.page-honeypot .hp-status__live-val,
body.page-honeypot .hp-status__live-best {
  white-space: nowrap;
}

body.page-honeypot .hp-status__prov {
  font-size: 11px;
}

body.page-honeypot .hp-status__note {
  margin-top: 8px;
  font: 400 12px/1.4 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-status.is-live .hp-status__dot {
  background: var(--color-accent);
  animation: hp-blink 2s steps(2, jump-none) infinite;
}

body.page-honeypot .hp-status.is-live .hp-status__word,
body.page-honeypot .hp-status.is-quiet .hp-status__word {
  color: var(--color-accent);
}

body.page-honeypot .hp-status.is-quiet .hp-status__dot {
  background: var(--color-accent);
}

body.page-honeypot .hp-status.is-stale .hp-status__dot,
body.page-honeypot .hp-status.is-nodata .hp-status__dot {
  background: var(--hp-orange);
}

body.page-honeypot .hp-status.is-stale .hp-status__word,
body.page-honeypot .hp-status.is-nodata .hp-status__word {
  color: var(--hp-orange-text);
}

body.page-honeypot .hp-status.is-offline .hp-status__dot,
body.page-honeypot .hp-status.is-schema .hp-status__dot {
  background: var(--color-error);
}

/* PAUSE is reachable from states whose word outranks it (WAITING FOR FIRST
   ATTEMPT, QUIET), so the dot carries the pause when the word cannot. */
body.page-honeypot .hp-status.is-paused-state .hp-status__dot {
  background: var(--color-text-secondary);
  animation: none;
}

body.page-honeypot .hp-status.is-offline .hp-status__word,
body.page-honeypot .hp-status.is-schema .hp-status__word {
  color: var(--color-error);
}

@keyframes hp-blink {
  from { opacity: 1; }
  to { opacity: 0.35; }
}

/* --- Mini play-by-play (desktop only) --- */
body.page-honeypot .hp-mini {
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
}

body.page-honeypot .hp-mini__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  height: 28px;
  padding: 0 12px;
  border-bottom: 1px solid var(--color-border);
  font: 500 12px/1 var(--font-mono);
  text-transform: uppercase;
  color: var(--color-text);
}

body.page-honeypot .hp-mini__link {
  font: 400 12px/1 var(--font-sans);
  text-transform: none;
  color: var(--color-accent);
  /* 40px target (spec 9) without growing the 28px panel header: the hit area
     bleeds into the panel border and the first tail row, neither of which is
     interactive. */
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 40px;
  margin: -6px 0;
}

body.page-honeypot .hp-mini__list {
  list-style: none;
  min-height: calc(5 * var(--hp-mini-row));
}

body.page-honeypot .hp-mini__row {
  display: grid;
  grid-template-columns: 60px 28px minmax(0, 1fr) 70px;
  align-items: center;
  gap: 8px;
  height: var(--hp-mini-row);
  padding: 0 12px 0 9px;
  border-left: 3px solid transparent;
  font: 400 12px/1 var(--font-mono);
  color: var(--color-text);
  white-space: nowrap;
}

body.page-honeypot .hp-mini__row + .hp-mini__row {
  border-top: 1px solid var(--color-border);
}

body.page-honeypot .hp-mini__row.is-new {
  border-left-color: var(--color-accent);
}

body.page-honeypot .hp-mini__t {
  font-size: 11px;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-mini__cred {
  overflow: hidden;
  text-overflow: ellipsis;
  direction: ltr;
  unicode-bidi: isolate;
}

body.page-honeypot .hp-mini__row .hp-chip {
  justify-self: end;
  font-size: 10px;
}

body.page-honeypot .hp-mini__empty {
  padding: 8px 12px;
  font: 400 12px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

/* --- Box score tiles --- */
body.page-honeypot .hp-tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--hp-gutter);
  margin-top: -8px;
}

body.page-honeypot .hp-tile {
  display: flex;
  flex-direction: column;
  /* The three line boxes already carry their own leading; a 4px gap on top
     pushed the strip 9px past the 100px it is budgeted in spec 2.1 and cost
     the podium the same 9px. */
  gap: 0;
  padding: 16px 20px;
  border: 1px solid var(--color-border);
  border-top-width: 1px;
  background: var(--color-bg-elevated);
  min-width: 0;
}

body.page-honeypot .hp-tile.is-current {
  border-top: 2px solid var(--color-accent);
  padding-top: 15px;
}

body.page-honeypot .hp-tile__label {
  font: 600 11px/1 var(--font-sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-tile__num {
  font: 700 36px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.page-honeypot .hp-tile.is-loading .hp-tile__num {
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-tile__sub {
  font: 400 12px/1.4 var(--font-mono);
  color: var(--color-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- Window toggle --- */
body.page-honeypot .hp-window {
  display: flex;
  border: 0;
  min-width: 0;
}

body.page-honeypot .hp-window input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  opacity: 0;
}

body.page-honeypot .hp-window label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  min-width: 64px;
  padding: 0 12px;
  margin-left: -1px;
  border: 1px solid var(--color-border-strong);
  font: 700 12px/1 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  background: transparent;
  cursor: pointer;
  user-select: none;
}

body.page-honeypot .hp-window label:first-of-type {
  margin-left: 0;
}

body.page-honeypot .hp-window label:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

body.page-honeypot .hp-window input:checked + label {
  position: relative;
  z-index: 1;
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-bg);
}

body.page-honeypot .hp-window input:focus-visible + label {
  outline: 2px solid var(--color-accent-hover);
  outline-offset: 2px;
  position: relative;
  z-index: 2;
}

body.page-honeypot .hp-window input:disabled + label {
  opacity: 0.55;
  cursor: default;
}

/* --- Podium --- */
body.page-honeypot .hp-podium {
  display: grid;
  grid-template-columns: 1fr 1.25fr 1fr;
  align-items: end;
  gap: var(--hp-gutter);
  max-width: 900px;
  margin: 8px auto 0;
  list-style: none;
}

body.page-honeypot .hp-step {
  display: flex;
  flex-direction: column;
  min-width: 0;
  --h: 200px;
}

body.page-honeypot .hp-step.rank-1 { order: 2; --h: 200px; }
body.page-honeypot .hp-step.rank-2 { order: 1; --h: 140px; }
body.page-honeypot .hp-step.rank-3 { order: 3; --h: 100px; }

body.page-honeypot .hp-step__top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  /* 12px here plus the old 1.3/1.1 leading put the step blocks 51px lower
     than the 628-912 slot in spec 2.1, which cut ranks 2 and 3 off the
     first screen entirely. */
  padding: 0 8px 8px;
  text-align: center;
  min-width: 0;
}

/* Scoped through .hp-step on purpose: these elements also carry .hp-val,
   whose `font: inherit` sits later in this file at equal specificity and was
   winning, so the podium values rendered in Inter 400 at 16px instead of
   JetBrains Mono 500 at 24/28px. Every override below needs the same
   ancestor or it loses to this rule. */
body.page-honeypot .hp-step .hp-step__val {
  font: 500 24px/1.2 var(--font-mono);
  color: var(--color-text);
  text-align: center;
}

body.page-honeypot .hp-step.rank-1 .hp-step__val {
  font-size: 28px;
}

body.page-honeypot .hp-step__count {
  font: 700 32px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

body.page-honeypot .hp-step.rank-2 .hp-step__count,
body.page-honeypot .hp-step.rank-3 .hp-step__count {
  font-size: 28px;
}

body.page-honeypot .hp-step__unit {
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-step__block {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: var(--h);
  border: 1px solid var(--color-border);
  border-top: 4px solid var(--hp-rank-1);
  background: var(--color-bg-elevated);
  transition: height 400ms cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;
}

body.page-honeypot .hp-step.rank-2 .hp-step__block {
  border-top-color: var(--hp-rank-2);
  transition-delay: 80ms;
}

body.page-honeypot .hp-step.rank-3 .hp-step__block {
  border-top-color: var(--hp-rank-3);
  transition-delay: 160ms;
}

body.page-honeypot .hp-step__num {
  font: 800 min(120px, calc(var(--h) - 12px)) / 0.8 var(--font-sans);
  color: var(--hp-rank-1);
  /* Bottom-centre per spec 5.6, standing 12px off the floor so the numerals
     clear the fold while the blocks themselves run 12px past it (spec 2.1). */
  padding-bottom: 12px;
}

body.page-honeypot .hp-step.rank-2 .hp-step__num,
body.page-honeypot .hp-step.rank-3 .hp-step__num {
  font-size: min(96px, calc(var(--h) - 12px));
}

body.page-honeypot .hp-step.rank-2 .hp-step__num { color: var(--hp-rank-2); }
body.page-honeypot .hp-step.rank-3 .hp-step__num { color: var(--hp-rank-3); }

body.page-honeypot .hp-step.is-loading .hp-step__val,
body.page-honeypot .hp-step.is-loading .hp-step__count,
body.page-honeypot .hp-step.is-empty .hp-step__val {
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-podium__empty,
body.page-honeypot .hp-podium__streak {
  margin-top: 16px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-podium__streak code {
  font: 400 13px/1.5 var(--font-mono);
  color: var(--color-text);
}

/* --- Ranked tables (standings, countries, ips) --- */
body.page-honeypot .hp-standings {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--hp-gutter);
  margin-top: 16px;
  --hp-bar-w: 72px;
}

body.page-honeypot .hp-standings .hp-row__n {
  width: calc(var(--hp-bar-w) + 64px + 16px);
}

body.page-honeypot .hp-standings .hp-cell-n span {
  width: 64px;
}

body.page-honeypot .hp-standings .hp-row__rank {
  /* Was 36px: with 8px padding that left a 20px content box, and two 18px
     digits need ~23px, so every rank from 10 up was clipped by the
     overflow: hidden below. */
  width: 40px;
}

body.page-honeypot .hp-list {
  min-width: 0;
}

body.page-honeypot .hp-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

body.page-honeypot .hp-table thead tr {
  height: 0;
}

body.page-honeypot .hp-table thead th {
  height: 0;
  padding: 0;
  line-height: 0;
  overflow: hidden;
  border: 0;
}

body.page-honeypot .hp-table__cap {
  height: 32px;
  padding: 0 8px;
  text-align: left;
  vertical-align: bottom;
  font: 500 12px/32px var(--font-mono);
  text-transform: uppercase;
  color: var(--color-text);
  caption-side: top;
}

body.page-honeypot .hp-row {
  height: var(--hp-row);
  border-top: 1px solid var(--color-border);
}

body.page-honeypot .hp-row:hover {
  background: var(--color-bg-hover);
}

body.page-honeypot .hp-row td,
body.page-honeypot .hp-row th {
  padding: 0 8px;
  vertical-align: middle;
  text-align: left;
  font-weight: 400;
  overflow: hidden;
}

/* Beats the font-weight: 400 above, which the .hp-row__rank shorthand below
   lost to on specificity, flattening the rank column to regular weight. */
body.page-honeypot .hp-row th.hp-row__rank {
  font-weight: 800;
}

body.page-honeypot .hp-row__rank {
  width: 40px;
  font: 800 18px/1 var(--font-sans);
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}

body.page-honeypot .hp-row.is-leader .hp-row__rank {
  color: var(--color-accent);
}

body.page-honeypot .hp-row__val {
  font: 400 14px/20px var(--font-mono);
  color: var(--color-text);
}

body.page-honeypot .hp-row__name {
  font: 500 14px/20px var(--font-sans);
  color: var(--color-text);
  white-space: nowrap;
  text-overflow: ellipsis;
}

body.page-honeypot .hp-row__cc {
  width: 48px;
}

body.page-honeypot .hp-row__n {
  width: calc(var(--hp-bar-w) + 72px + 24px);
}

body.page-honeypot .hp-cell-n {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

body.page-honeypot .hp-cell-n span {
  width: 72px;
  text-align: right;
  font: 500 14px/20px var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-bar {
  width: var(--hp-bar-w);
  height: 6px;
  background: var(--color-border);
  flex-shrink: 0;
}

body.page-honeypot .hp-bar__fill {
  width: var(--w, 0%);
  height: 100%;
  background: var(--hp-bar);
  transition: width 400ms cubic-bezier(0.4, 0, 0.2, 1);
}

body.page-honeypot .is-leader .hp-bar__fill {
  background: var(--color-accent);
}

body.page-honeypot .hp-row__chg {
  width: 44px;
  font: 700 11px/1 var(--font-sans);
  color: var(--color-text-secondary);
  text-align: right;
  white-space: nowrap;
}

body.page-honeypot .hp-table.no-change .hp-row__chg,
body.page-honeypot .hp-table.no-change th.hp-row__chg {
  display: none;
}

body.page-honeypot .hp-row__chg.is-up { color: var(--color-accent); }
body.page-honeypot .hp-row__chg.is-down { color: var(--color-text-secondary); }
body.page-honeypot .hp-row__chg.is-new { color: var(--hp-orange-text); }
body.page-honeypot .hp-row__chg.is-same { color: var(--color-text-secondary); }

body.page-honeypot .hp-row--empty td {
  padding: 8px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

/* --- Value cells (attacker strings) --- */
body.page-honeypot .hp-val {
  display: block;
  width: 100%;
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: inherit;
  overflow: hidden;
  direction: ltr;
  unicode-bidi: isolate;
}

body.page-honeypot .hp-val__t {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

body.page-honeypot button.hp-val {
  cursor: pointer;
}

/* The row is 36px (spec 5.7); without this the button was a 20px-tall strip
   inside it. Flex keeps the expanded, wrapping state working. */
body.page-honeypot .hp-row button.hp-val {
  display: flex;
  align-items: center;
  min-height: 36px;
}

body.page-honeypot button.hp-val:hover .hp-val__t {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

body.page-honeypot .hp-val.is-expanded .hp-val__t {
  display: block;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  text-overflow: clip;
  -webkit-line-clamp: unset;
}

body.page-honeypot .hp-row.is-expanded td {
  padding-top: 8px;
  padding-bottom: 8px;
}

body.page-honeypot .hp-val.is-empty-value,
body.page-honeypot .hp-empty-marker {
  font-style: italic;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-colon {
  color: var(--color-text-secondary);
}

/* --- Play-by-play tail --- */
body.page-honeypot .hp-tail {
  list-style: none;
  margin-top: 8px;
}

body.page-honeypot .hp-tail__row {
  display: grid;
  /* Two 1fr value columns split one credential across ~350px of nothing at
     1440. The eighth track is empty and absorbs the slack, which is where
     the 4.4 wireframe puts it. */
  grid-template-columns: 84px 56px 32px 140px minmax(0, 140px) minmax(0, 200px) 88px 1fr;
  align-items: center;
  gap: 8px;
  min-height: var(--hp-tail-row);
  padding: 0 8px 0 5px;
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--color-border);
  font: 400 13px/18px var(--font-mono);
  color: var(--color-text);
}

body.page-honeypot .hp-tail__row.is-new {
  border-left-color: var(--color-accent);
}

body.page-honeypot .hp-tail__row.is-new.is-flash {
  animation: hp-flash 600ms ease-out 1;
}

@keyframes hp-flash {
  from { background: var(--color-accent-soft); }
  to { background: transparent; }
}

body.page-honeypot .hp-tail__t {
  font-size: 12px;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

body.page-honeypot .hp-tail__ip,
body.page-honeypot .hp-tail__u,
body.page-honeypot .hp-tail__p {
  font: inherit;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  direction: ltr;
  unicode-bidi: isolate;
}

body.page-honeypot .hp-tail__cred {
  display: contents;
}

body.page-honeypot .hp-tail__colon {
  display: none;
}

body.page-honeypot .hp-tail__row .hp-tail__res {
  justify-self: start;
}

body.page-honeypot .hp-tail__row.is-overflow {
  display: block;
  padding: 0 8px;
  line-height: var(--hp-tail-row);
  font-size: 12px;
  color: var(--color-text-secondary);
  border-left-color: transparent;
}

body.page-honeypot .hp-tail__empty,
body.page-honeypot .hp-list__empty {
  padding: 12px 8px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

/* --- Two-column sections --- */
body.page-honeypot .hp-half {
  grid-column: span 6;
}

/* The 4.4 wireframe reads `1 [CN] China    ████  40,112` with the slack
   trailing at the right edge. A 1fr name column instead put 300px of nothing
   between "China" and its bar. Only wide desktops have slack to move: below
   ~1160px the panel is narrower than name + bar + count, so the flexible
   column is right there and this must not apply. */
@media (min-width: 1200px) {
  body.page-honeypot #countries .hp-table,
  body.page-honeypot #ips .hp-table {
    table-layout: auto;
  }

  /* width:1px in an auto table means "as narrow as the content allows"; with
     the nowrap already on the cell that is the widest country name. */
  body.page-honeypot #countries .hp-row__name {
    width: 1px;
  }

  body.page-honeypot #ips .hp-row__val {
    width: 200px;
  }

  body.page-honeypot #countries .hp-row__n,
  body.page-honeypot #ips .hp-row__n {
    width: 100%;
  }

  body.page-honeypot #countries .hp-cell-n,
  body.page-honeypot #ips .hp-cell-n {
    justify-content: flex-start;
  }
}

body.page-honeypot .hp-third {
  grid-column: span 4;
}

body.page-honeypot .hp-two-thirds {
  grid-column: span 8;
}

/* --- Splits --- */
body.page-honeypot .hp-splits {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--hp-gutter);
  margin-top: 16px;
}

body.page-honeypot .hp-splits__hours { grid-column: span 6; min-width: 0; }
body.page-honeypot .hp-splits__days { grid-column: span 3; min-width: 0; }
body.page-honeypot .hp-splits__side {
  grid-column: span 3;
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

body.page-honeypot .hp-histo {
  margin: 0;
}

body.page-honeypot .hp-histo__cap,
body.page-honeypot .hp-panel__cap {
  font: 400 12px/1.4 var(--font-mono);
  color: var(--color-text-secondary);
  margin-bottom: 12px;
}

body.page-honeypot .hp-histo__bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: var(--hp-histo-h);
  border-bottom: 1px solid var(--color-border);
  list-style: none;
}

body.page-honeypot .hp-histo__bars li {
  flex: 1 1 0;
  min-width: 0;
  height: var(--h, 30%);
  min-height: 2px;
  background: var(--hp-bar);
  transition: height 400ms cubic-bezier(0.4, 0, 0.2, 1);
}

body.page-honeypot .hp-histo.is-loading .hp-histo__bars li {
  background: var(--color-border-strong);
}

body.page-honeypot .hp-histo__bars li.bar--peak {
  background: var(--color-accent);
}

body.page-honeypot .hp-histo__bars li.bar--current {
  background: repeating-linear-gradient(45deg, var(--hp-orange) 0 3px, transparent 3px 6px);
  border-top: 1px solid var(--hp-orange);
}

body.page-honeypot .hp-histo.is-empty .hp-histo__bars li {
  height: 0;
  min-height: 0;
}

body.page-honeypot .hp-histo__axis {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font: 400 11px/1 var(--font-mono);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-histo__axis--days span {
  flex: 1 1 0;
  text-align: center;
}

body.page-honeypot .hp-histo__note,
body.page-honeypot .hp-histo__record,
body.page-honeypot .hp-histo__empty {
  margin-top: 8px;
  font: 400 12px/1.4 var(--font-mono);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-histo__note .hp-hatch {
  color: var(--hp-orange-text);
}

body.page-honeypot .hp-proto__bar {
  display: flex;
  height: 24px;
  background: var(--color-border);
  overflow: hidden;
}

body.page-honeypot .hp-proto__seg {
  width: var(--w, 0%);
  height: 100%;
  transition: width 400ms cubic-bezier(0.4, 0, 0.2, 1);
}

body.page-honeypot .hp-proto__seg--ssh { background: var(--color-accent); }
body.page-honeypot .hp-proto__seg--telnet { background: var(--hp-orange); }
body.page-honeypot .hp-proto__seg--other { background: var(--color-border-strong); }

body.page-honeypot .hp-proto__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin-top: 8px;
  font: 400 13px/1.5 var(--font-mono);
  color: var(--color-text);
  list-style: none;
}

body.page-honeypot .hp-proto__legend li {
  display: flex;
  align-items: center;
  gap: 6px;
}

body.page-honeypot .hp-swatch {
  width: 8px;
  height: 8px;
  flex-shrink: 0;
}

body.page-honeypot .hp-swatch--ssh { background: var(--color-accent); }
body.page-honeypot .hp-swatch--telnet { background: var(--hp-orange); }
body.page-honeypot .hp-swatch--other { background: var(--color-border-strong); }

body.page-honeypot .hp-panel__joke,
body.page-honeypot .hp-accepted__cap {
  margin-top: 8px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-accepted__label {
  font: 600 11px/1 var(--font-sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-accepted__num {
  margin-top: 6px;
  font: 700 36px/1.1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

body.page-honeypot .hp-accepted.is-loading .hp-accepted__num {
  color: var(--color-text-secondary);
}

/* --- Scouts: connected, never tried a credential ---
   Deliberately quiet. This is context for the loud panels above it, not a headline, so the
   number sits at tile-sub scale rather than the 36px the ACCEPTED panel uses. */
body.page-honeypot .hp-scouts {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--color-border);
}

body.page-honeypot .hp-scouts__line {
  margin-top: 6px;
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-wrap: wrap;
}

body.page-honeypot .hp-scouts__num {
  font: 700 22px/1.1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

body.page-honeypot .hp-scouts__num--sm {
  font-size: 15px;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-scouts__lbl {
  font: 400 12px/1.4 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-scouts__ccs {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
}

body.page-honeypot .hp-scouts__cc {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 5px;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
}

body.page-honeypot .hp-scouts__cc-n {
  font: 400 11px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-scouts__empty {
  margin-top: 8px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-scouts.is-loading .hp-scouts__num {
  color: var(--color-text-secondary);
}

/* --- Debuts + interview --- */
body.page-honeypot .hp-cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--hp-gutter);
  margin-top: 16px;
}

body.page-honeypot .hp-cols.is-single {
  grid-template-columns: minmax(0, 1fr);
}

body.page-honeypot .hp-col__cap {
  height: 32px;
  padding: 0 8px;
  font: 500 12px/32px var(--font-mono);
  text-transform: uppercase;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.page-honeypot .hp-plain {
  list-style: none;
}

body.page-honeypot .hp-debut {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-height: 44px;
  padding: 4px 8px;
  border-top: 1px solid var(--color-border);
  font: 400 14px/20px var(--font-mono);
  color: var(--color-text);
}

body.page-honeypot .hp-debut time {
  font-size: 11px;
  line-height: 14px;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

body.page-honeypot .hp-debut--none {
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-honeypot .hp-cmd {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  align-items: start;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  font: 400 13px/18px var(--font-mono);
  color: var(--color-text);
}

body.page-honeypot .hp-cmd + .hp-cmd {
  margin-top: 4px;
}

body.page-honeypot .hp-cmd--latest {
  grid-template-columns: 60px 28px minmax(0, 1fr);
}

body.page-honeypot .hp-cmd__n {
  text-align: right;
  font-size: 12px;
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}

body.page-honeypot .hp-cmd__t {
  font-size: 11px;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

body.page-honeypot .hp-cmd .hp-val__t {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  text-overflow: clip;
}

/* --- Skeleton --- */
body.page-honeypot .hp-skel {
  display: block;
  background: var(--color-bg-hover);
}

body.page-honeypot .hp-skel--rows {
  --rows: 25;
  --row-h: var(--hp-row);
  height: calc(var(--rows) * var(--row-h));
  background: repeating-linear-gradient(
    to bottom,
    var(--color-bg-hover) 0 calc(var(--row-h) - 1px),
    var(--color-bg) calc(var(--row-h) - 1px) var(--row-h)
  );
}

body.page-honeypot .hp-skel--tail {
  --row-h: var(--hp-tail-row);
}

body.page-honeypot .hp-skel--mini {
  --rows: 5;
  --row-h: var(--hp-mini-row);
}

body.page-honeypot .hp-skel--ten { --rows: 10; }
body.page-honeypot .hp-skel--cmd { --rows: 10; --row-h: 40px; }

/* --- Footer --- */
body.page-honeypot .hp-footer {
  color: var(--color-text-secondary);
  text-align: left;
  font: 400 13px/1.6 var(--font-sans);
}

body.page-honeypot .hp-footer__inner {
  max-width: var(--hp-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

body.page-honeypot .hp-footer a {
  color: var(--color-accent);
}

body.page-honeypot .hp-footer a:hover {
  color: var(--color-accent-hover);
}

/* --- Screen-reader status --- */
body.page-honeypot .hp-sr-status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

/* ============================================
   Tablet (769-1024)
   ============================================ */
@media (max-width: 1024px) {
  body.page-honeypot .hp-hero__main,
  body.page-honeypot .hp-hero__side {
    grid-column: 1 / -1;
    max-width: none;
  }

  body.page-honeypot .hp-hero__num {
    font-size: 96px;
    min-height: 96px;
  }

  body.page-honeypot .hp-hero__num.is-len-8 { font-size: 72px; }
  body.page-honeypot .hp-hero__num.is-len-10 { font-size: 56px; }

  body.page-honeypot .hp-hero__cap { font-size: 16px; }

  body.page-honeypot .hp-mini {
    display: none;
  }

  /* Status panel collapses to one line under the caption. */
  body.page-honeypot .hp-status {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    padding: 0;
    border: 0;
    background: transparent;
  }

  body.page-honeypot .hp-status__head,
  body.page-honeypot .hp-status__meta,
  body.page-honeypot .hp-status__delta {
    display: contents;
  }

  body.page-honeypot .hp-status__lbl,
  body.page-honeypot .hp-status__prov,
  body.page-honeypot .hp-status__sep {
    display: none;
  }

  body.page-honeypot .hp-status__updated::before,
  body.page-honeypot .hp-status__delta-n::before,
  body.page-honeypot .hp-status__next::before {
    content: "\00b7\00a0";
    color: var(--color-text-secondary);
  }

  /* One-line order: dot, word, time, +N, countdown, button (spec 5.8). */
  body.page-honeypot .hp-status__dot { order: 0; }
  body.page-honeypot .hp-status__word { order: 1; }
  body.page-honeypot .hp-status__updated { order: 2; }
  body.page-honeypot .hp-status__delta-n { order: 3; }
  body.page-honeypot .hp-status__next { order: 4; }

  /* One flex item rather than display:contents, so its own labels and
     separators keep their spacing. It is the only item whose width varies
     with the data ("FIRST TOUCH 2.7 s · FIRST GUESS 47 s · fastest yet" is
     353 px at 400 px wide), so it takes a row under the strip: inline it
     leaves too little room for PAUSE and strands the button on a third,
     otherwise empty, line. min-width:0 lets its text wrap instead of
     widening the panel. */
  body.page-honeypot .hp-status__live {
    order: 15;
    flex-basis: 100%;
    min-width: 0;
    margin: 0;
  }

  body.page-honeypot .hp-status__btn {
    order: 10;
    margin-left: auto;
    border: 0;
    padding: 0 4px;
  }

  body.page-honeypot .hp-status__note {
    flex-basis: 100%;
    margin: 0;
    /* Without an order the note computes 0, lands on the dot's flex line,
       cannot fit at 100% basis and wraps - leaving the state square alone on
       line 1, 44px from the word it colours. After the button (10) it takes
       its own line under the strip, which is what spec 5.8 draws. */
    order: 20;
  }

  body.page-honeypot .hp-tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.page-honeypot .hp-section__head h2 { font-size: 24px; }

  body.page-honeypot .hp-step__num,
  body.page-honeypot .hp-step.rank-2 .hp-step__num,
  body.page-honeypot .hp-step.rank-3 .hp-step__num {
    font-size: min(72px, calc(var(--h) - 12px));
  }

  body.page-honeypot .hp-step.rank-1 .hp-step__num {
    font-size: min(96px, calc(var(--h) - 12px));
  }

  body.page-honeypot .hp-step .hp-step__val { font-size: 18px; }
  body.page-honeypot .hp-step.rank-1 .hp-step__val { font-size: 20px; }

  body.page-honeypot .hp-standings {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.page-honeypot .hp-standings > .hp-list:last-child {
    grid-column: 1 / -1;
  }

  body.page-honeypot .hp-splits__hours { grid-column: 1 / -1; }
  body.page-honeypot .hp-splits__days,
  body.page-honeypot .hp-splits__side { grid-column: span 6; }

  body.page-honeypot .hp-third,
  body.page-honeypot .hp-two-thirds {
    grid-column: 1 / -1;
  }
}

/* ============================================
   Phone (<= 768)
   ============================================ */
@media (max-width: 768px) {
  body.page-honeypot {
    --hp-gutter: 16px;
    --hp-bar-w: 64px;
    --hp-histo-h: 120px;
  }

  body.page-honeypot .hp-page {
    padding: 16px var(--hp-gutter) 40px;
    row-gap: 20px;
  }

  body.page-honeypot .hp-hero {
    row-gap: 0;
  }

  body.page-honeypot .hp-hero__side {
    margin-top: 8px;
  }

  /* Spec 2.2 orders the hero eyebrow, number, caption, one-line status, then
     the disclosure. The status lives in the other hero column, so the left
     column dissolves into the hero grid and the disclosure sorts after it. */
  body.page-honeypot .hp-hero__main {
    display: contents;
  }

  body.page-honeypot .hp-hero__main > * {
    grid-column: 1 / -1;
  }

  body.page-honeypot .hp-hero__side {
    order: 1;
  }

  body.page-honeypot .hp-hero__disclosure {
    order: 2;
  }

  body.page-honeypot .hp-page > .hp-section {
    /* 20px row-gap + this = the 24px of spec 4.3. */
    margin-top: 4px;
  }

  body.page-honeypot .hp-eyebrow { font-size: 11px; }

  body.page-honeypot .hp-hero__num {
    font-size: 64px;
    min-height: 64px;
    margin: 4px 0 4px;
  }

  body.page-honeypot .hp-hero__num.is-len-8 { font-size: 52px; }
  body.page-honeypot .hp-hero__num.is-len-10 { font-size: 40px; }

  body.page-honeypot .hp-hero__cap { font-size: 15px; }

  body.page-honeypot .hp-hero__disclosure {
    margin-top: 8px;
    font-size: 13px;
    line-height: 1.4;
  }

  body.page-honeypot .hp-tiles {
    gap: 8px;
  }

  /* Three stacked lines cannot fit the 56px tile of spec 2.2 (11 + 26 + 17 +
     padding = 70), so the label and its sub-line share the top row and the
     number owns the second. */
  body.page-honeypot .hp-tile {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    padding: 6px 12px;
    gap: 0 8px;
  }

  body.page-honeypot .hp-tile.is-current {
    padding-top: 5px;
  }

  body.page-honeypot .hp-tile__label {
    grid-column: 1;
    grid-row: 1;
  }

  body.page-honeypot .hp-tile__sub {
    grid-column: 2;
    grid-row: 1;
    text-align: right;
  }

  body.page-honeypot .hp-tile__num {
    grid-column: 1 / -1;
    grid-row: 2;
    font-size: 24px;
    line-height: 1.1;
  }

  body.page-honeypot .hp-tile__sub-extra,
  body.page-honeypot .hp-tile__sub-lead {
    display: none;
  }

  body.page-honeypot .hp-section__head h2 { font-size: 22px; }

  body.page-honeypot .hp-section__titles {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  body.page-honeypot .hp-section__head {
    gap: 8px 16px;
    padding-bottom: 4px;
  }

  body.page-honeypot .hp-section__ctl {
    margin-left: 0;
    flex-basis: 100%;
  }

  body.page-honeypot .hp-window {
    width: 100%;
  }

  body.page-honeypot .hp-window label {
    flex: 1 1 0;
    min-width: 0;
    padding: 0;
  }

  /* Podium becomes the medal stack. */
  body.page-honeypot .hp-podium {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    max-width: none;
    margin-top: 8px;
  }

  body.page-honeypot .hp-step {
    display: grid;
    grid-template-columns: 48px minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 8px;
    min-height: 56px;
    padding: 4px 12px 4px 0;
    border: 1px solid var(--color-border);
    border-left: 4px solid var(--hp-rank-1);
    background: var(--color-bg-elevated);
    order: 0 !important;
  }

  body.page-honeypot .hp-step.rank-2 { border-left-color: var(--hp-rank-2); }
  body.page-honeypot .hp-step.rank-3 { border-left-color: var(--hp-rank-3); }

  body.page-honeypot .hp-step__top,
  body.page-honeypot .hp-step__block {
    display: contents;
  }

  body.page-honeypot .hp-step__num,
  body.page-honeypot .hp-step.rank-1 .hp-step__num,
  body.page-honeypot .hp-step.rank-2 .hp-step__num,
  body.page-honeypot .hp-step.rank-3 .hp-step__num {
    order: 1;
    padding: 0;
    font-size: 48px;
    line-height: 1;
    text-align: center;
  }

  body.page-honeypot .hp-step .hp-step__val,
  body.page-honeypot .hp-step.rank-1 .hp-step__val {
    order: 2;
    font-size: 18px;
    line-height: 1.3;
    text-align: left;
  }

  body.page-honeypot .hp-step__val .hp-val__t {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    white-space: normal;
    overflow-wrap: anywhere;
    text-overflow: clip;
  }

  body.page-honeypot .hp-step__count,
  body.page-honeypot .hp-step.rank-2 .hp-step__count,
  body.page-honeypot .hp-step.rank-3 .hp-step__count {
    order: 3;
    font-size: 20px;
    text-align: right;
  }

  body.page-honeypot .hp-step__unit {
    display: none;
  }

  body.page-honeypot .hp-standings {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }

  body.page-honeypot .hp-row__n {
    width: calc(var(--hp-bar-w) + 64px + 16px);
  }

  body.page-honeypot .hp-cell-n span {
    width: 64px;
  }

  body.page-honeypot .hp-row__rank {
    width: 36px;
    font-size: 16px;
  }

  body.page-honeypot .hp-row__chg {
    width: 40px;
  }

  body.page-honeypot .hp-row td,
  body.page-honeypot .hp-row th {
    padding: 0 6px;
  }

  /* Tail rows reflow to two lines. */
  body.page-honeypot .hp-tail__row {
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    grid-template-areas:
      "t cc ip proto"
      "cred cred cred res";
    row-gap: 2px;
    min-height: 48px;
    padding: 6px 8px 6px 5px;
    font-size: 12px;
    line-height: 16px;
  }

  body.page-honeypot .hp-tail__t { grid-area: t; }
  body.page-honeypot .hp-tail__row .hp-cc { grid-area: cc; }
  body.page-honeypot .hp-tail__ip { grid-area: ip; }
  body.page-honeypot .hp-tail__proto { grid-area: proto; justify-self: end; }
  body.page-honeypot .hp-tail__row .hp-tail__res { grid-area: res; justify-self: end; }

  body.page-honeypot .hp-tail__cred {
    display: block;
    grid-area: cred;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    direction: ltr;
    unicode-bidi: isolate;
  }

  body.page-honeypot .hp-tail__cred .hp-tail__u,
  body.page-honeypot .hp-tail__cred .hp-tail__p {
    display: inline;
  }

  body.page-honeypot .hp-tail__colon {
    display: inline;
    color: var(--color-text-secondary);
  }

  body.page-honeypot .hp-splits {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }

  body.page-honeypot .hp-splits__hours,
  body.page-honeypot .hp-splits__days,
  body.page-honeypot .hp-splits__side {
    grid-column: 1 / -1;
  }

  body.page-honeypot .hp-histo__bars {
    gap: 1px;
  }

  body.page-honeypot .hp-cols {
    grid-template-columns: minmax(0, 1fr);
  }

  body.page-honeypot .hp-half {
    grid-column: 1 / -1;
  }

  /* The header is static at this width, so the anchor offset above would only
     leave dead air over the target. */
  body.page-honeypot #board,
  body.page-honeypot .hp-page section[id] {
    scroll-margin-top: 0;
  }

  body.page-honeypot .hp-skel--rows { --rows: 10; }
  body.page-honeypot .hp-skel--tail { --rows: 15; }
  body.page-honeypot .hp-skel--ten { --rows: 8; }
  body.page-honeypot .hp-skel--cmd { --rows: 6; }
}

/* ============================================
   Preferences
   ============================================ */
@media (prefers-contrast: more) {
  body.page-honeypot {
    --color-text-secondary: #e5e5e5;
    --color-border: #404040;
    --color-border-strong: #6b6b6b;
    --hp-bar: rgba(34, 197, 94, 0.85);
    --hp-link-underline: var(--color-accent);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Selectors mirror the animating rules' specificity so these win outright
     instead of relying on the global duration collapse in styles.css. */
  body.page-honeypot .hp-status.is-live .hp-status__dot,
  body.page-honeypot .hp-tail__row.is-new.is-flash,
  body.page-honeypot .hp-mini__row.is-new.is-flash,
  body.page-honeypot .hp-section.is-ready {
    animation: none;
  }

  body.page-honeypot .hp-step__block,
  body.page-honeypot .hp-bar__fill,
  body.page-honeypot .hp-proto__seg,
  body.page-honeypot .hp-histo__bars li {
    transition: none;
  }
}

/* Defensive: .sr-only relies on width/height 1px to collapse the box, but those are
   minimums on table and table-caption display types, so the element keeps its full
   intrinsic size and, being absolutely positioned, extends the page. Any .sr-only that
   is itself a table gets flattened to a block; wrap the table in a plain .sr-only div
   instead when the table semantics matter (see the histogram tables in index.html). */
body.page-honeypot table.sr-only,
body.page-honeypot caption.sr-only {
  display: block;
}

/* ============================================
   Sessions (/sessions)
   Reuses the board's tokens and chip/table components; only the card, tag and
   terminal shapes are new. No radii, no blue, no yellow, same as everything else.
   ============================================ */

body.page-sessions .hp-sessions {
  max-width: 960px;
}

body.page-sessions .hp-sessions__status {
  margin: 24px 0;
  font: 400 15px/1.6 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-sessions .hp-sessions__controls {
  margin-top: 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Outlined in both states, accented when on. A solid fill here would be the only
   filled control on the page and would read as a heading rather than a switch. */
body.page-sessions .hp-filter {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 14px;
  border: 1px solid var(--color-border-strong);
  background: none;
  font: 700 12px/1 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  cursor: pointer;
}

body.page-sessions .hp-filter:hover {
  color: var(--color-text);
}

/* Every control on these pages draws its own focus ring. Without one the browser draws its
   own, and Firefox's is its accent blue. */
body.page-sessions .hp-filter:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.page-sessions .hp-filter[aria-pressed="true"] {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

body.page-sessions .hp-filter[aria-pressed="true"]::before {
  content: "\2713";
  font-size: 13px;
}

body.page-sessions .hp-filter__count {
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--color-text-secondary);
}

body.page-sessions .hp-sessions__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-top: 24px;
}

body.page-sessions .hp-card {
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  padding: 16px 18px;
}

body.page-sessions .hp-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-border);
}

body.page-sessions .hp-card__time {
  font: 500 13px/1 var(--font-mono);
  color: var(--color-text);
}

body.page-sessions .hp-card__ip {
  font: 400 13px/1 var(--font-mono);
  color: var(--color-text-secondary);
  /* Attacker-supplied: isolate so a bidi trick cannot reorder the row. */
  direction: ltr;
  unicode-bidi: isolate;
}

body.page-sessions .hp-card__dur {
  margin-left: auto;
  font: 400 12px/1 var(--font-mono);
  color: var(--color-text-secondary);
}

body.page-sessions .hp-card__opening {
  margin-top: 14px;
  font: 500 16px/1.5 var(--font-sans);
  color: var(--color-text);
}

body.page-sessions .hp-card__repeat {
  margin-top: 6px;
  font: 400 13px/1.5 var(--font-sans);
  color: var(--color-accent);
}

body.page-sessions .hp-card__narrative {
  margin-top: 10px;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

body.page-sessions .hp-card__narrative li {
  font: 400 14px/1.55 var(--font-sans);
  color: var(--color-text-secondary);
  padding-left: 14px;
  position: relative;
}

body.page-sessions .hp-card__narrative li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 6px;
  height: 1px;
  background: var(--color-accent);
}

body.page-sessions .hp-card__tags {
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
}

body.page-sessions .hp-tag {
  font: 700 10px/1 var(--font-sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-strong);
  padding: 3px 6px;
}

body.page-sessions .hp-card__actions {
  margin-top: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

body.page-sessions .hp-card__btn {
  width: auto;
  border: 1px solid var(--color-border-strong);
  border-top: 1px solid var(--color-border-strong);
  padding: 0 12px;
  min-height: 40px;
}

/* Sits in the button row, so it takes the buttons' height and centres against them.
   Left on its own baseline it made the row look ragged. */
body.page-sessions .hp-card__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  margin-left: auto;
  padding: 0 4px;
  font: 700 11px/1 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

body.page-sessions .hp-card__link:hover,
body.page-sessions .hp-card__link:focus-visible {
  color: var(--color-accent);
}

/* The colour alone left the ring to the browser: blue, in Firefox. */
body.page-sessions .hp-card__link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* The card a link points at gets focus from js/sessions.js (focusCard) on arrival and on back
   and forward. With no ring of its own, Firefox draws its accent blue around it. */
body.page-sessions .hp-card:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* The card someone was sent to: a flat fill, the site's mark for the current item, never an
   edge or a bar. Mixed into the card's own background, so its text keeps AA contrast
   (secondary grey 4.9:1, green 5.6:1). */
body.page-sessions .hp-card.is-linked {
  background: color-mix(in srgb, var(--color-accent) 15%, var(--color-bg-elevated));
}

body.page-sessions .hp-card__archived {
  margin-bottom: 12px;
  font: 400 12px/1.5 var(--font-sans);
  color: var(--color-accent);
}

body.page-sessions .hp-card__norec {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 4px;
  font: 400 12px/1 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-sessions .hp-card__panel {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--color-border);
}

body.page-sessions .hp-card__cred {
  font: 400 13px/1.5 var(--font-mono);
  color: var(--color-text);
  padding: 4px 10px 4px 0;
  direction: ltr;
  unicode-bidi: isolate;
  overflow-wrap: anywhere;
}

body.page-sessions .hp-card__cred-ok {
  font: 700 10px/1 var(--font-sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

body.page-sessions tr.is-accepted .hp-card__cred-ok {
  color: var(--hp-orange-text);
}

body.page-sessions .hp-card__cmds {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-left: 22px;
}

body.page-sessions .hp-card__cmds li {
  font: 400 13px/1.5 var(--font-mono);
  color: var(--color-text);
  overflow-wrap: anywhere;
  direction: ltr;
  unicode-bidi: isolate;
}

body.page-sessions .hp-card__cmds-more,
body.page-sessions .hp-card__creds-more {
  margin-top: 8px;
  font: 400 12px/1.4 var(--font-sans);
  color: var(--color-text-secondary);
}

/* How many of a whole-address card's sessions used this credential or command.
   Absent everywhere else, where every row happened exactly once. */
body.page-sessions .hp-card__cred-n,
body.page-sessions .hp-card__cmd-n {
  font: 400 12px/1 var(--font-mono);
  color: var(--color-text-secondary);
  white-space: nowrap;
}

body.page-sessions .hp-card__cred-n {
  padding-left: 14px;
  text-align: right;
}

body.page-sessions .hp-card__cmd-n {
  margin-left: 10px;
}

/* --- terminal ---
   The transcript player: on the /sessions cards, and in the replays on the /attacks
   pages, which reuse this markup and these rules (sizes for long-form reading are set
   in the attack pages block at the end). */
body.page-sessions .hp-term,
body.page-attacks .hp-term {
  border: 1px solid var(--color-border-strong);
  background: var(--color-bg);
}

body.page-sessions .hp-term__bar,
body.page-attacks .hp-term__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--color-border);
}

body.page-sessions .hp-term__play,
body.page-sessions .hp-term__all,
body.page-attacks .hp-term__play,
body.page-attacks .hp-term__all {
  font: 700 11px/1 var(--font-sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-strong);
  background: none;
  padding: 0 10px;
  min-height: 32px;
}

body.page-sessions .hp-term__play:hover,
body.page-sessions .hp-term__all:hover,
body.page-attacks .hp-term__play:hover,
body.page-attacks .hp-term__all:hover {
  color: var(--color-text);
}

/* The attack pages set this ring in their own block; the cards on /sessions had none, so the
   browser drew its own there (blue, in Firefox). */
body.page-sessions .hp-term__play:focus-visible,
body.page-sessions .hp-term__all:focus-visible,
body.page-sessions .hp-term__screen:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.page-sessions .hp-term__play[aria-pressed="true"],
body.page-attacks .hp-term__play[aria-pressed="true"] {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

body.page-sessions .hp-term__meta,
body.page-attacks .hp-term__meta {
  margin-left: auto;
  font: 400 11px/1 var(--font-mono);
  color: var(--color-text-secondary);
}

body.page-sessions .hp-term__screen,
body.page-attacks .hp-term__screen {
  margin: 0;
  padding: 12px;
  min-height: 160px;
  max-height: 420px;
  overflow: auto;
  font: 400 13px/1.5 var(--font-mono);
  color: var(--color-accent);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* The transcript is attacker-authored text: isolate it so nothing inside can
     reorder the surrounding page. */
  direction: ltr;
  unicode-bidi: isolate;
}

body.page-sessions .hp-term__note,
body.page-attacks .hp-term__note {
  padding: 8px 12px;
  border-top: 1px solid var(--color-border);
  font: 400 12px/1.4 var(--font-sans);
  color: var(--hp-orange-text);
}

@media (max-width: 768px) {
  body.page-sessions .hp-card {
    padding: 14px;
  }

  body.page-sessions .hp-card__dur {
    margin-left: 0;
    flex-basis: 100%;
  }

  body.page-sessions .hp-card__opening {
    font-size: 15px;
  }

  body.page-sessions .hp-term__screen {
    font-size: 12px;
    max-height: 320px;
  }
}

/* ============================================
   Start here: the way from the board and from /sessions into /attacks
   ============================================ */

/* Outlined rather than filled: a lone filled box among the outlined controls around it
   reads as a label, not a link. 44px tall so a thumb can find it. */
body.page-honeypot .hp-start {
  margin-top: 16px;
}

body.page-honeypot .hp-start a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 8px 14px;
  border: 1px solid var(--color-accent);
  font: 600 1rem/1.3 var(--font-sans);
  color: var(--color-accent);
}

body.page-honeypot .hp-start a:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent-hover);
}

body.page-honeypot .hp-start a:focus-visible,
body.page-honeypot .hp-accepted__cap a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* The word "rigged" in the board's accepted panel links to the paragraph that says how. */
body.page-honeypot .hp-accepted__cap a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ============================================
   Attack pages (/attacks and /attacks/<slug>)
   Long-form reading, built by scripts/build-attacks.py: one column at a readable measure,
   the board's palette, square corners, and nothing a reader has to read below 1rem.
   Mobile first; wider screens only get more room. The live counts and the replay player
   are filled in by js/attacks.js; this block styles their containers and the notes.
   ============================================ */

/* The board's skip link is 13px capitals; here it leads into an article and is read at the
   article's size. */
body.page-attacks .hp-skip {
  font-size: 1rem;
}

body.page-attacks .hp-article {
  /* About 70 characters of Inter at 1rem. */
  max-width: 46rem;
  margin: 0 auto;
  padding: 24px 16px 48px;
  font: 400 1rem/1.7 var(--font-sans);
  color: var(--color-text);
  overflow-wrap: break-word;
}

body.page-attacks .hp-article__title {
  margin: 8px 0 28px;
  font: 800 1.875rem/1.15 var(--font-sans);
  letter-spacing: 0.01em;
  color: var(--color-text);
}

body.page-attacks .hp-article section {
  margin-bottom: 44px;
}

body.page-attacks .hp-article section h2 {
  margin-bottom: 16px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--color-border);
  font: 800 1.375rem/1.25 var(--font-sans);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-text);
}

body.page-attacks .hp-article section h3 {
  margin: 28px 0 10px;
  font: 700 1.125rem/1.35 var(--font-sans);
  color: var(--color-text);
}

body.page-attacks .hp-article section p,
body.page-attacks .hp-article section ul,
body.page-attacks .hp-article section ol,
body.page-attacks .hp-article section pre,
body.page-attacks .hp-article section table,
body.page-attacks .hp-article section .hp-callout {
  margin-bottom: 16px;
}

body.page-attacks .hp-article section li > :last-child,
body.page-attacks .hp-article section .hp-callout > :last-child {
  margin-bottom: 0;
}

body.page-attacks .hp-article section strong {
  font-weight: 700;
  color: var(--color-text);
}

/* Links in running text are underlined as well as green, so they do not depend on colour
   alone to be seen. */
body.page-attacks .hp-article a {
  color: var(--color-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--hp-link-underline);
  text-underline-offset: 3px;
}

body.page-attacks .hp-article a:hover {
  color: var(--color-accent-hover);
  text-decoration-color: currentColor;
}

body.page-attacks .hp-article a:focus-visible,
body.page-attacks .hp-footer a:focus-visible,
body.page-attacks .hp-term__play:focus-visible,
body.page-attacks .hp-term__all:focus-visible,
body.page-attacks .hp-term__screen:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Source marks are already bracketed; an underline under [12] is noise. A run of them
   ([2][20]) is wrapped by the build in .hp-cites and kept on one line. */
body.page-attacks .hp-article a[href^="#src-"] {
  text-decoration: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

body.page-attacks .hp-article .hp-cites {
  white-space: nowrap;
}

body.page-attacks .hp-article section ul,
body.page-attacks .hp-article section ol {
  padding-left: 1.5rem;
}

body.page-attacks .hp-article section ul {
  list-style: square;
}

body.page-attacks .hp-article section li + li {
  margin-top: 8px;
}

body.page-attacks .hp-article section li > p,
body.page-attacks .hp-article section li > pre {
  margin-bottom: 8px;
}

body.page-attacks .hp-article section ul > li::marker {
  color: var(--color-accent);
}

body.page-attacks .hp-article section ol > li::marker {
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}

/* Commands and file names. Wrapped rather than scrolled sideways, so nothing on a phone
   hides past the edge of the screen, and isolated, so no character in one can reorder the
   text around it. */
body.page-attacks .hp-article section code,
body.page-attacks .hp-article section pre {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  overflow-wrap: anywhere;
}

body.page-attacks .hp-article section code {
  font-size: max(1rem, 0.95em);
  padding: 0 4px;
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
}

body.page-attacks .hp-article section pre {
  padding: 12px 14px;
  font-size: 1rem;
  line-height: 1.55;
  white-space: pre-wrap;
  color: var(--color-text);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border-strong);
}

/* Boxes the text wants read first: how the rigged login works, the key check, the lockout
   warning. Orange stands where a warning yellow would. */
body.page-attacks .hp-callout {
  padding: 12px;
  background: var(--color-bg-elevated);
  border: 1px solid var(--hp-orange);
}

body.page-attacks .hp-article section .hp-callout pre,
body.page-attacks .hp-article section .hp-callout code {
  background: var(--color-bg);
}

body.page-attacks .hp-article table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

/* Words are never split inside a cell: "00:00:0" over "1" is no way to read a time. A
   table that cannot fit then scrolls inside .hp-table-scroll, never the page. */
body.page-attacks .hp-article .hp-table-scroll {
  margin-bottom: 16px;
  overflow-x: auto;
}

body.page-attacks .hp-article .hp-table-scroll:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.page-attacks .hp-article section .hp-table-scroll > table {
  margin-bottom: 0;
}

body.page-attacks .hp-article th,
body.page-attacks .hp-article td {
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  vertical-align: top;
}

body.page-attacks .hp-article th {
  font-weight: 700;
  color: var(--color-text-secondary);
  border-bottom-color: var(--color-border-strong);
}

/* The sources. The item a [N] mark was followed to is filled, so the reader can find it in
   a list of thirty. */
body.page-attacks .hp-article #sources ol {
  /* Room for two-digit numbers inside the column rather than hanging out of it. */
  padding-left: 2.25rem;
}

body.page-attacks #sources ol > li {
  padding: 2px 6px;
  margin-left: -6px;
  color: var(--color-text-secondary);
}

body.page-attacks #sources ol > li:target {
  color: var(--color-text);
  background: var(--color-accent-soft);
}

/* The live counts (spec 13.2). js/attacks.js fills them; until then, and when they fail,
   only the status line shows. */
body.page-attacks .hp-live {
  margin-bottom: 44px;
}

body.page-attacks #this-week .hp-live {
  margin-bottom: 0;
}

body.page-attacks .hp-live__status {
  padding: 12px 14px;
  font: 400 1rem/1.6 var(--font-sans);
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-strong);
}

/* The replay and its notes. The terminal is the /sessions player's own markup and rules
   (see "terminal" above); here it gets reading sizes. */
body.page-attacks .hp-walk {
  margin-bottom: 44px;
}

body.page-attacks .hp-replay {
  margin-bottom: 20px;
}

body.page-attacks .hp-term__bar {
  flex-wrap: wrap;
}

/* 1rem, like every other word on these pages: the bar wraps, so the room is there. */
body.page-attacks .hp-term__play,
body.page-attacks .hp-term__all {
  min-height: 40px;
  padding: 0 14px;
  font-size: 1rem;
  cursor: pointer;
}

body.page-attacks .hp-term__meta,
body.page-attacks .hp-term__note {
  font-size: 1rem;
  line-height: 1.4;
}

body.page-attacks .hp-term__screen {
  min-height: 12rem;
  max-height: 28rem;
  font-size: 1rem;
}

body.page-attacks .hp-replay__noscript {
  padding: 12px 14px;
  font: 400 1rem/1.6 var(--font-sans);
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-strong);
  border-top: 0;
}

/* Without JavaScript the player is a box of dead buttons, so it goes, and the note under
   it says where the lines are. Browsers that cannot answer this query keep the box. */
@media (scripting: none) {
  body.page-attacks .hp-replay .hp-term {
    display: none;
  }

  body.page-attacks .hp-replay__noscript {
    border-top: 1px solid var(--color-border-strong);
  }
}

body.page-attacks .hp-article .hp-notes {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

body.page-attacks .hp-notes .hp-note {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--color-border);
}

/* The typed line itself, above what it does. Attacker-authored, so isolated like the
   transcript it came from. */
body.page-attacks .hp-notes .hp-note > code {
  display: block;
  margin-bottom: 8px;
  padding: 8px 10px;
  font: 400 1rem/1.5 var(--font-mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  direction: ltr;
  unicode-bidi: isolate;
  color: var(--color-accent);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
}

/* The line the replay has reached. A flat fill, the way every current item on the site is
   marked. */
body.page-attacks .hp-notes .hp-note.is-current {
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}

body.page-attacks .hp-next {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 8px;
  padding-top: 24px;
  border-top: 1px solid var(--color-border);
}

body.page-attacks .hp-next a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 8px 16px;
  font: 600 1rem/1.3 var(--font-sans);
  color: var(--color-text);
  text-decoration: none;
  border: 1px solid var(--color-border-strong);
}

body.page-attacks .hp-next .hp-next__link {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

body.page-attacks .hp-next a:hover {
  background: var(--color-bg-hover);
}

/* The same column as the article: 46rem wide with the article's own side padding, so the
   footer's left edge lines up with the text above it. */
body.page-attacks .hp-footer.hp-footer--article {
  padding: 24px 0;
  font: 400 1rem/1.6 var(--font-sans);
}

body.page-attacks .hp-footer--article .hp-footer__inner {
  max-width: 46rem;
  padding: 0 16px;
  gap: 8px;
}

/* Wherever the phone layout's (max-width: 768px) does not apply. 768.02px rather than 769px:
   browser zoom and display scaling give fractional widths such as 768.18px, which match
   neither of two integer bounds (partials/header.css says more). */
@media (min-width: 768.02px) {
  body.page-attacks .hp-article {
    padding: 40px 24px 64px;
  }

  body.page-attacks .hp-callout {
    padding: 16px 18px;
  }

  /* The header is sticky from here up, so an anchor (a [N] mark, a type link from a
     session card, the skip link) has to land below it rather than behind it. On phones
     these pages scroll the header away (see the max-width: 768px rule at the top). */
  body.page-attacks .hp-article,
  body.page-attacks .hp-article [id] {
    scroll-margin-top: 80px;
  }

  body.page-attacks .hp-article__title {
    font-size: 2.5rem;
  }

  body.page-attacks .hp-article h2 {
    font-size: 1.5rem;
  }

  body.page-attacks .hp-footer.hp-footer--article {
    padding: 32px 0;
  }

  body.page-attacks .hp-footer--article .hp-footer__inner {
    padding: 0 24px;
  }
}

/* --- contents ---
   Every section with an h2, then the attack pages, written twice by scripts/build-attacks.py:
   a sidebar beside the article from 1100px (nav.hp-toc), and below that a box under the h1
   (details.hp-toc-inline), closed until it is opened. js/modules/honeypot/toc.js marks the
   section being read in both with aria-current="location", and the build marks the link to
   the page itself with aria-current="page". Either is a current item, so either is a flat
   fill and never a line. */
body.page-attacks .hp-toc__head {
  margin: 0 0 6px;
  padding: 0 10px;
  font: 700 1rem/1.4 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text);
}

body.page-attacks .hp-toc__list + .hp-toc__head {
  margin-top: 24px;
}

body.page-attacks .hp-toc__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* As specific as .hp-article a and later than it, so the copy inside the article does not
   wear the underline and the green of a link in running text. A long entry wraps. */
body.page-attacks .hp-toc__list a {
  display: block;
  padding: 6px 10px;
  font: 400 1rem/1.4 var(--font-sans);
  color: var(--color-text-secondary);
  text-decoration: none;
  overflow-wrap: break-word;
}

body.page-attacks .hp-toc__list a:hover {
  color: var(--color-text);
}

/* Any value: "location" for the section being read, "page" for this page. */
body.page-attacks .hp-toc__list a[aria-current] {
  color: var(--color-accent);
  background: var(--color-accent-soft);
}

/* Inset, so the sidebar's own scroll box cannot clip it. */
body.page-attacks .hp-toc__list a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* The box under the h1. 1099.98px rather than 1099px: zoom and display scaling give
   fractional widths, and the sidebar's (min-width: 1100px) has to meet this exactly. */
@media (max-width: 1099.98px) {
  body.page-attacks .hp-toc {
    display: none;
  }

  body.page-attacks .hp-toc-inline {
    margin: 0 0 40px;
    border: 1px solid var(--color-border-strong);
    background: var(--color-bg-elevated);
  }

  body.page-attacks .hp-toc-inline[open] {
    padding-bottom: 12px;
  }

  /* The whole row opens and closes the box, 44px tall for a thumb. Its marker is drawn here
     rather than left to the browser, which draws it differently in each engine. */
  body.page-attacks .hp-toc-inline > summary {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding: 10px 14px;
    font: 700 1rem/1.4 var(--font-sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text);
    list-style: none;
    cursor: pointer;
  }

  body.page-attacks .hp-toc-inline > summary::-webkit-details-marker {
    display: none;
  }

  body.page-attacks .hp-toc-inline > summary::before {
    content: "\25B8";
    width: 1rem;
    font-size: 1.25rem;
    line-height: 1;
    color: var(--color-accent);
  }

  body.page-attacks .hp-toc-inline[open] > summary::before {
    content: "\25BE";
  }

  body.page-attacks .hp-toc-inline > summary:hover {
    background: var(--color-bg-hover);
  }

  body.page-attacks .hp-toc-inline > summary:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
  }

  body.page-attacks .hp-toc-inline[open] > summary {
    margin-bottom: 4px;
  }

  /* The summary says "On this page" right above the first list, so the first group's heading
     is not drawn a second time under it. The list keeps its name (aria-label). */
  body.page-attacks .hp-toc-inline > .hp-toc__head:first-of-type {
    display: none;
  }

  body.page-attacks .hp-toc-inline > .hp-toc__head,
  body.page-attacks .hp-toc-inline > .hp-toc__list {
    margin-left: 4px;
    margin-right: 4px;
  }
}

/* The sidebar. The article keeps its column and its measure, 46rem, and the sidebar takes
   14rem to its left, the pair centred together. Sticky below the header, which is 64px tall,
   and never taller than the screen under it: a list longer than that scrolls inside its own
   box, and while it does, the box does not hand its scrolling on to the page. */
@media (min-width: 1100px) {
  body.page-attacks .hp-attack-layout {
    display: grid;
    grid-template-columns: 14rem minmax(0, 46rem);
    column-gap: 3rem;
    justify-content: center;
  }

  body.page-attacks .hp-toc {
    position: sticky;
    top: 80px;
    align-self: start;
    /* Level with the h1: the article's 40px top padding and the h1's 8px top margin. */
    margin-top: 48px;
    max-height: calc(100vh - 96px);
    max-height: calc(100dvh - 96px);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--color-border-strong) transparent;
  }

  /* Only while the list is longer than the box, which js/modules/honeypot/toc.js says with
     this class. On a box with nothing to scroll, contain swallows the wheel in Chromium: with
     the pointer over the sidebar, the page would not scroll at all. */
  body.page-attacks .hp-toc.is-scrollable {
    overscroll-behavior: contain;
  }

  body.page-attacks .hp-toc-inline {
    display: none;
  }

  /* The footer's text starts where the article's does (see the footer above): the same 63rem
     as the layout, centred the same way, and the sidebar and the gap left empty. */
  body.page-attacks .hp-footer--article .hp-footer__inner {
    max-width: 63rem;
    padding-left: calc(17rem + 24px);
  }
}

/* ============================================
   Attack types on the session cards, and this week's counts on the /attacks pages
   Everything js/sessions.js and js/attacks.js build: the attack line under a card's head
   chips (badges, and a whole-address card's mix), the live rows with their bars, the
   board measures, the daily bars and the replay's loading states. The note a playing
   replay has reached (.hp-note.is-current) is styled with the notes above. Every word at
   least 1rem, square corners, green for links and bars, orange where a caveat needs seeing.
   ============================================ */

/* --- the attack line on a card --- */
body.page-sessions .hp-card__attack {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: 12px;
}

body.page-sessions .hp-card__attack + .hp-card__opening {
  margin-top: 10px;
}

body.page-sessions .hp-card__attack-type {
  font: 500 1rem/1.5 var(--font-sans);
  color: var(--color-text);
}

body.page-sessions .hp-card__attack-label {
  color: var(--color-text-secondary);
}

body.page-sessions .hp-card__attack-link {
  color: var(--color-accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  /* The token, so prefers-contrast: more raises it with every other link's underline. */
  text-decoration-color: var(--hp-link-underline);
  text-underline-offset: 3px;
}

body.page-sessions .hp-card__attack-link:hover {
  color: var(--color-accent-hover);
  text-decoration-color: currentColor;
}

body.page-sessions .hp-card__attack-link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.page-sessions .hp-card__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
}

/* Outlined and quiet: a badge adds to the type, it never outranks it. */
body.page-sessions .hp-badge {
  padding: 1px 8px;
  border: 1px solid var(--color-border-strong);
  font: 400 1rem/1.5 var(--font-sans);
  color: var(--color-text-secondary);
}

/* --- this week, on every /attacks page --- */
body.page-attacks .hp-live__lede {
  margin-bottom: 16px;
  font: 600 1.125rem/1.5 var(--font-sans);
  color: var(--color-text);
}

/* "Counting since": the numbers below cover less than the week they are labelled with. Also
   the line above everything else when the document has stopped being updated
   (.hp-live__stale). */
body.page-attacks .hp-live__note {
  margin-bottom: 16px;
  padding: 10px 14px;
  font: 400 1rem/1.6 var(--font-sans);
  color: var(--color-text);
  border: 1px solid var(--hp-orange);
}

/* A time in a sentence ("2026-10-17 00:02 UTC") stays on one line; at the hyphens it would
   otherwise break into "2026-10-" and "17". */
body.page-attacks .hp-live__time {
  white-space: nowrap;
}

/* The rows. Numbers right-aligned in tabular figures so they compare down the column, and
   never split; the bar sits under the name, which keeps three columns inside a 320px
   screen. A week too big even for that scrolls inside .hp-live__scroll, never the page. */
body.page-attacks .hp-article .hp-live__scroll {
  /* The 4px each side is room for a focused link's outline, which the box would clip. */
  margin: 0 -4px 28px;
  padding: 0 4px;
  overflow-x: auto;
}

body.page-attacks .hp-article .hp-live__scroll:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.page-attacks .hp-article .hp-live__types {
  width: 100%;
  border-collapse: collapse;
  font: 400 1rem/1.5 var(--font-sans);
  font-variant-numeric: tabular-nums;
}

body.page-attacks .hp-article .hp-live__types th,
body.page-attacks .hp-article .hp-live__types td {
  padding: 10px 14px 10px 0;
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  vertical-align: top;
}

body.page-attacks .hp-article .hp-live__types th:last-child,
body.page-attacks .hp-article .hp-live__types td:last-child {
  padding-right: 0;
}

body.page-attacks .hp-article .hp-live__types thead th {
  font-weight: 700;
  color: var(--color-text-secondary);
  border-bottom-color: var(--color-border-strong);
}

body.page-attacks .hp-article .hp-live__types .hp-live__type {
  width: 100%;
  font-weight: 500;
  color: var(--color-text);
}

body.page-attacks .hp-article .hp-live__types .hp-live__num {
  text-align: right;
  white-space: nowrap;
}

body.page-attacks .hp-live__bar {
  display: block;
  height: 6px;
  margin-top: 8px;
  background: var(--color-border);
}

body.page-attacks .hp-live__fill {
  display: block;
  width: var(--w, 0%);
  height: 100%;
  background: var(--color-accent);
}

/* A count that is not zero is never drawn as nothing, however small beside the largest. */
body.page-attacks .hp-live__fill.is-some {
  min-width: 2px;
}

/* The board's own numbers, boxed apart from the rows: they count different things and do
   not add up with them. */
body.page-attacks .hp-live__measures {
  padding: 14px 16px;
  border: 1px solid var(--color-border-strong);
}

body.page-attacks .hp-live__heading {
  margin-bottom: 10px;
  font: 700 1.125rem/1.35 var(--font-sans);
  color: var(--color-text);
}

body.page-attacks .hp-live__dl {
  margin: 0;
}

body.page-attacks .hp-live__measure {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 2px 16px;
  padding: 6px 0;
  border-bottom: 1px solid var(--color-border);
  font: 400 1rem/1.5 var(--font-sans);
}

body.page-attacks .hp-live__measure:last-child {
  border-bottom: 0;
}

body.page-attacks .hp-live__measure dt {
  color: var(--color-text-secondary);
}

body.page-attacks .hp-live__measure dd {
  margin: 0;
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}

/* The daily bars on the two deep pages with a replay. One grid for the whole list, so
   every bar starts and ends at the same place whatever the day's label or count. The bars
   keep at least 3.5rem: on a narrow screen the longest count ("19,264 from 01:00") gives up
   width first, putting its words under the number, rather than leaving bars too short to
   compare. */
body.page-attacks .hp-article .hp-live__label {
  margin-bottom: 8px;
  font: 700 1rem/1.4 var(--font-sans);
  color: var(--color-text-secondary);
}

body.page-attacks .hp-article .hp-live__days {
  display: grid;
  grid-template-columns: 6.5rem minmax(3.5rem, 1fr) auto;
  gap: 6px 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

body.page-attacks .hp-article .hp-live__day {
  display: grid;
  grid-column: 1 / -1;
  /* Browsers without subgrid keep the first value: bars still start together. */
  grid-template-columns: 6.5rem minmax(3.5rem, 1fr) 7.5rem;
  grid-template-columns: subgrid;
  align-items: center;
  margin: 0;
  font: 400 1rem/1.5 var(--font-sans);
  font-variant-numeric: tabular-nums;
}

body.page-attacks .hp-live__day .hp-live__date {
  color: var(--color-text-secondary);
  white-space: nowrap;
}

body.page-attacks .hp-live__day .hp-live__bar {
  height: 10px;
  margin-top: 0;
}

/* A count too long for the row breaks inside itself rather than pushing the page sideways. */
body.page-attacks .hp-live__day .hp-live__num {
  text-align: right;
  overflow-wrap: anywhere;
  color: var(--color-text);
}

/* "from 01:00" and "so far" move under their number whole, never split in two. */
body.page-attacks .hp-live__day .hp-live__when {
  display: inline-block;
  white-space: nowrap;
}

/* A part day: today, which is not over ("so far"), or the first day, which the week starts
   partway into ("from 01:00"). Its bar is the paler green. */
body.page-attacks .hp-live__day.is-partial .hp-live__fill {
  background: var(--hp-bar);
}

/* --- the replay while it loads, and when it cannot --- */
body.page-attacks .hp-term__play:disabled,
body.page-attacks .hp-term__all:disabled {
  cursor: default;
  opacity: 0.5;
}

body.page-attacks .hp-replay__status {
  padding: 12px 14px;
  font: 400 1rem/1.6 var(--font-sans);
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-strong);
}

/* 768.02px, not 769px: the phone layout is (max-width: 768px), and a zoomed or scaled screen
   can be 768.18px wide, which an integer bound here would leave in neither. */
@media (min-width: 768.02px) {
  body.page-attacks .hp-live__lede {
    font-size: 1.25rem;
  }

  body.page-attacks .hp-live__measures {
    padding: 16px 20px;
  }
}
