/*
 * The console's stylesheet.
 *
 * LIGHT, and deliberately not theme-following: this sits beside the Cloudflare
 * dashboard all day and is mostly dense tables of small text read while
 * something is going wrong. One committed palette, every colour chosen against
 * white — rather than two half-tuned ones neither of which is quite right.
 */
:root{
    --bg:#f7f7f7;          /* page */
    --panel:#fff;          /* cards, tables */
    --ink:#1d1d1f;         /* body text */
    --ink-2:#4a4a4f;       /* secondary */
    --mut:#77777d;         /* labels, captions */
    --line:#e3e3e6;        /* hairlines */
    --line-2:#d3d3d8;      /* stronger edges */
    --acc:#f6821f;         /* Cloudflare orange, used sparingly */
    --ok:#12784a;
    --bad:#c0392b;
    --shadow:0 1px 2px rgba(0,0,0,.04);
  }
  *{box-sizing:border-box}
  /**
   * ALWAYS RESERVE THE SCROLLBAR TRACK.
   *
   * The five views are wildly different heights — Addresses is long, Admins is
   * three rows — so switching tabs added and removed the scrollbar, and every
   * time it did, the whole page jumped sideways by its width. Now that each
   * view is a real URL the effect is worse: it happens on Back and Forward too.
   *
   * `overflow-y: scroll`, not `scrollbar-gutter: stable`: see
   * ~/.claude/skills/web-layout-stability. The companion rule that skill
   * prescribes — cancelling react-remove-scroll's `body[data-scroll-locked]`
   * padding — is deliberately absent, because this console has no Radix and no
   * JS overlay: its two dialogs are the browser's own confirm() and alert(),
   * which do not touch the document.
   */
  html{-webkit-text-size-adjust:100%;overflow-y:scroll}
  body{
    margin:0;background:var(--bg);color:var(--ink);
    font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    -webkit-font-smoothing:antialiased;
  }
  .wrap{max-width:1100px;margin:0 auto;padding:26px 22px 90px}

  header{
    display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;
    padding-bottom:18px;margin-bottom:26px;border-bottom:1px solid var(--line-2)
  }
  h1{font-size:18px;font-weight:600;margin:0;letter-spacing:-.01em}
  .sub{font-size:13px;color:var(--mut);margin-top:2px}
  h2{
    font-size:12px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
    color:var(--mut);margin:34px 0 10px
  }
  p{margin:0 0 14px}
  .muted{color:var(--mut);font-size:13px}

  /* Stat tiles. The number leads; the label explains. */
  .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}
  .card{
    background:var(--panel);border:1px solid var(--line);border-radius:8px;
    padding:16px 18px;box-shadow:var(--shadow)
  }
  .n{
    font-size:28px;font-weight:600;letter-spacing:-.02em;line-height:1.1;
    font-variant-numeric:tabular-nums
  }
  .lbl{font-size:12.5px;color:var(--mut);margin-top:4px}

  /* Wide content scrolls in its own box; the page never does. */
  .scroll{overflow-x:auto;background:var(--panel);border:1px solid var(--line);
          border-radius:8px;box-shadow:var(--shadow)}
  table{border-collapse:collapse;width:100%;font-size:13.5px;min-width:660px}
  th{
    text-align:left;font-size:11.5px;font-weight:600;letter-spacing:.04em;
    text-transform:uppercase;color:var(--mut);padding:11px 16px;
    background:#fafafa;border-bottom:1px solid var(--line);white-space:nowrap
  }
  td{padding:12px 16px;border-bottom:1px solid var(--line);vertical-align:middle}
  tr:last-child td{border-bottom:0}
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px}
  td.nowrap{white-space:nowrap}
  .ok{color:var(--ok);font-weight:500}
  .bad{color:var(--bad);font-weight:500}
  .tag{
    display:inline-block;font-size:11px;padding:2px 8px;border-radius:20px;
    background:#f0f0f2;color:var(--ink-2);border:1px solid var(--line)
  }

  button{
    font:inherit;font-size:13px;font-weight:500;padding:8px 14px;border-radius:6px;
    border:1px solid var(--line-2);background:#fff;color:var(--ink);cursor:pointer;
    min-height:36px;transition:background .12s,border-color .12s
  }
  button:hover{background:#fafafa;border-color:#bfbfc4}
  button.primary{background:var(--acc);border-color:var(--acc);color:#fff}
  button.primary:hover{background:#e0741a;border-color:#e0741a}
  button.link{border:0;background:none;color:var(--acc);padding:6px 4px;min-height:0}
  button:disabled{opacity:.55;cursor:default}
  button:focus-visible,input:focus-visible,select:focus-visible{
    outline:2px solid var(--acc);outline-offset:1px
  }

  label{display:block;font-size:12.5px;color:var(--mut);margin-bottom:5px}
  input,select{
    font:inherit;font-size:14px;padding:9px 11px;border:1px solid var(--line-2);
    border-radius:6px;background:#fff;color:var(--ink);width:100%
  }
  input::placeholder{color:#a6a6ac}

  /* `display` on a class beats the user agent's `[hidden]{display:none}`, so a
     `.row` marked hidden stayed on screen — which meant a viewer was shown the
     create-key form and the add-administrator form and only found out they
     could not use them by pressing the button. Caught by e2e; nothing about
     the page looked wrong. */
  [hidden]{display:none!important}
  .row{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;margin:14px 0 18px}
  .row>div{flex:1 1 200px}
  .row>div.fit{flex:0 0 auto}

  .secret{
    background:#fffaf4;border:1px solid #f3c99a;border-radius:8px;
    padding:14px 16px;margin:14px 0
  }
  .empty{color:var(--mut);padding:22px 16px;text-align:center;font-size:13.5px}

  /* The one-time secret, and its button. `min-width:0` on the code is what
     lets a 71-character token shrink and wrap instead of shoving the button
     off the panel — a flex item defaults to min-content width, which for an
     unbroken hex string is the whole string. */
  .secretrow{display:flex;align-items:flex-start;gap:12px;margin-top:6px}
  /* The config block handed over at key creation. Bordered off from the secret
     above it so the two are not mistaken for one blob to paste somewhere. */
  .onboard{margin-top:16px;padding-top:14px;border-top:1px solid #f0d6b4}
  .onboard pre{margin:0;white-space:pre-wrap;word-break:break-all;font-size:12px;line-height:1.6}
  .secretrow code{flex:1 1 auto;min-width:0;word-break:break-all}
  .secretrow button{flex:0 0 auto;min-width:86px}

  /* `Display Name <local@host>`, broken where it is already two things.
     One line let the column wrap mid-local-part — "EventBinder <event-" over
     "notices@..." — which reads as a corrupted value rather than a wrapped
     one. */
  .idwrap{display:block;min-width:0;flex:1 1 auto}
  .idname{display:block;line-height:1.35}
  .idaddr{display:block;color:var(--mut);overflow-wrap:anywhere;line-height:1.35}

  /* A disabled administrator is still a row worth reading — it says who had
     access and who granted it — so it is dimmed, not hidden. */
  tr.off td{opacity:.6}
  tr.off td .tag{opacity:1}
  /* The role picker sits inside a table cell and must not stretch it. */
  td select{width:auto;min-width:150px;padding:6px 9px;font-size:13px}

  /* The search box is the front door, so it is the largest control here. */
  .search{display:flex;gap:10px;margin:0 0 18px}
  .search input{
    flex:1 1 auto;font-size:16px;padding:13px 15px;border-radius:10px;
    border:1px solid var(--line-2);box-shadow:var(--shadow)
  }
  .search button{flex:0 0 auto;padding:0 22px;border-radius:10px;font-size:14px}

  /* One line, not a wall of tiles. Colour only when it means something. */
  .health{
    display:flex;gap:20px;align-items:center;flex-wrap:wrap;
    background:var(--panel);border:1px solid var(--line);border-radius:10px;
    padding:14px 18px;margin-bottom:12px;box-shadow:var(--shadow);font-size:13.5px
  }
  .health b{font-variant-numeric:tabular-nums;font-weight:600}
  .health .dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:7px}
  .health .dot.ok{background:var(--ok)} .health .dot.bad{background:var(--bad)}
  .health .dot.idle{background:#b8b8be}

  .quota{
    background:var(--panel);border:1px solid var(--line);border-radius:10px;
    padding:14px 18px;margin-bottom:18px;box-shadow:var(--shadow)
  }
  .qrow{display:flex;align-items:center;gap:12px;margin:9px 0;font-size:13px}
  .qname{flex:0 0 190px;color:var(--ink-2)}
  .qbar{flex:1 1 auto;height:7px;border-radius:99px;background:#eeeef0;overflow:hidden}
  .qfill{height:100%;background:var(--ok);border-radius:99px}
  .qfill.warn{background:#e8a33d} .qfill.hot{background:var(--bad)}
  .qnum{flex:0 0 auto;color:var(--mut);font-variant-numeric:tabular-nums}

  .bar{display:flex;justify-content:space-between;align-items:center;gap:12px;
       flex-wrap:wrap;margin:0 0 10px}
  .filters{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
  .filters select,.filters input[type=date]{width:auto;padding:7px 10px;border-radius:8px;font-size:13px}
  .inline{display:flex;align-items:center;gap:6px;margin:0;font-size:13px;color:var(--ink-2)}
  .inline input{width:auto}

  .more{display:flex;justify-content:center;margin:14px 0 0}
  .foot{margin-top:14px;font-size:12.5px}

  /* An expandable row: the forensic detail lives here, not in the table.
     NOT .row — that is the form-layout class below and sets display:flex,
     which turned every table row into a flex container and stacked the cells
     at the same x. A generic class name colliding with a structural one is the
     whole failure. */
  tr.logrow{cursor:pointer}
  tr.logrow:hover{background:#fafafa}
  tr.detail td{background:#fbfbfc;padding:0}
  .dgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
         gap:10px 26px;padding:16px 18px}
  /* Ids and error strings are long and unbreakable; without this they run
     under the next column instead of wrapping inside their own. */
  .dgrid div{font-size:12.5px;min-width:0;overflow-wrap:anywhere}
  .dgrid .k{color:var(--mut);display:block;margin-bottom:2px}

  .tabs{display:inline-flex;gap:2px;margin-right:14px}
  .tab{border-radius:7px;padding:6px 12px;min-height:32px;font-size:13px;
       border:1px solid transparent;background:none;color:var(--ink-2)}
  .tab[aria-current]{background:#fff;border-color:var(--line-2);color:var(--ink);font-weight:600}
  .who{display:flex;align-items:center;gap:12px;flex-wrap:wrap;font-size:13px;color:var(--mut)}

/* ======================================================= address cards ====
 * Written MOBILE-FIRST: the base rules are the phone, and the wider screens
 * are the media queries. The reverse — desktop first with phone overrides —
 * is how the previous pass produced a nine-column table that technically
 * scrolled on a phone and was unreadable on one.
 */
.cards{display:flex;flex-direction:column;gap:12px}

.card{
  background:var(--panel);border:1px solid var(--line);border-radius:14px;
  box-shadow:var(--shadow);overflow:hidden;padding:0 0 14px
}
.card-head{
  display:flex;flex-direction:column;gap:6px;align-items:flex-start;
  width:100%;background:none;border:0;text-align:left;cursor:pointer;
  /* 44px is the floor for a touch target, and this is the primary one. */
  padding:14px 16px 10px;min-height:44px;font:inherit;color:inherit
}
.card-head:hover{background:#fafafa}
.card-head .line1{display:flex;align-items:center;gap:8px;width:100%}
/* The address is the identity of the card and must not be the thing that
   truncates. It wraps instead — `anywhere` so it can shrink below its longest
   token, which is what actually removes the overflow on a 320px screen. */
.card-head .addr{font-size:15px;font-weight:600;overflow-wrap:anywhere;line-height:1.3}
.card-head .line2{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px}

.pill{
  display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:600;
  background:#eef3ee;color:var(--ok);white-space:nowrap
}
.pill.soft{background:#f0f0f2;color:var(--ink-2)}
.pill.bad{background:#fdecea;color:var(--bad)}
/* Amber, not red: "not configured" is a thing to look at, not a failure. */
.pill.warnp{background:#fdf3e7;color:#8a5216}

.nums{display:flex;gap:8px;padding:0 16px 12px}
.nums>div{
  flex:1;background:#fafafa;border:1px solid var(--line);border-radius:10px;
  padding:9px 10px;min-width:0
}
.nums b{display:block;font-size:19px;font-weight:600;font-variant-numeric:tabular-nums;line-height:1.15}
.nums span{display:block;font-size:11px;color:var(--mut);margin-top:2px}
.nums .bad b{color:var(--bad)}

.spark{display:block;width:calc(100% - 32px);height:34px;margin:0 16px}
.sp-ok{fill:#cfd9e6}
.sp-bad{fill:var(--bad)}
.sp-nil{fill:var(--line)}
.sparkax{display:flex;justify-content:space-between;padding:3px 16px 0}
.small{font-size:11px}

.logoprev{max-height:36px;max-width:220px;display:block;margin:2px 0 8px;
           border:1px solid var(--line);border-radius:4px;background:#fff;padding:4px}
.logorow{display:flex;gap:8px;align-items:center;margin:2px 0 4px}
.logorow input{flex:1 1 auto;min-width:0;font-size:13px;padding:7px 9px}
.logorow button{flex:0 0 auto}

.qwrap{padding:12px 16px 0}
/* The quota note. One line by default: the long version is true and important
   and almost nobody needs it on the way past, so it collapses. */
.qnote{margin-top:7px;line-height:1.5}
.qnote details{margin-top:3px}
/* 44px of vertical target on a phone. `padding:2px 0` gave it about 20, which
   is under every guideline and hard to hit one-handed at a venue — the exact
   situation this console is read in. */
.qnote summary{cursor:pointer;color:var(--acc);list-style:none;display:inline-block;
               padding:11px 0;min-height:22px}
.qnote summary::-webkit-details-marker{display:none}
.qnote summary::before{content:'\25B8 ';display:inline-block;transition:transform .12s}
.qnote details[open] summary::before{transform:rotate(90deg)}
.qnote details p{margin:6px 0 0}
.qhead{display:flex;justify-content:space-between;gap:10px;font-size:12px;margin-bottom:5px;flex-wrap:wrap}
.qbar{height:6px;background:#eee;border-radius:999px;overflow:hidden}
.qbar i{display:block;height:100%;background:var(--ok);border-radius:999px}
.qbar i.warn{background:var(--acc)}
.qbar i.hot{background:var(--bad)}

.card-more{padding:14px 16px 0;margin-top:12px;border-top:1px solid var(--line)}
.purpose{margin:0 0 12px;color:var(--ink-2);font-size:13px}
.facts{margin:0;display:grid;grid-template-columns:1fr;gap:2px 0}
.facts dt{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--mut);margin-top:12px}
.facts dd{margin:2px 0 0;font-size:13px;overflow-wrap:anywhere}
/* Inbound is the one fact most likely to be misread, so it is set apart rather
   than sitting in the run of the list looking like another metric. */
.facts dd.cant{display:flex;flex-direction:column;gap:4px;background:#fbfbfc;
  border:1px solid var(--line);border-left:3px solid var(--line-2);
  border-radius:0 10px 10px 0;padding:10px 12px}
.kline{display:flex;flex-direction:column;gap:2px;padding:7px 0;border-bottom:1px solid var(--line)}
.kline:last-child{border-bottom:0}
.kline code{font-size:12px;overflow-wrap:anywhere}

.alerts{
  background:#fff8f2;border:1px solid #f3d6bb;border-left:3px solid var(--acc);
  border-radius:0 12px 12px 0;padding:11px 14px;margin-bottom:14px;
  display:flex;flex-direction:column;gap:6px;font-size:13px
}
.skel{height:150px;border-radius:14px;background:linear-gradient(90deg,#f0f0f2,#f7f7f9,#f0f0f2);
  background-size:200% 100%;animation:sh 1.2s linear infinite;margin-bottom:12px}
@keyframes sh{to{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.skel{animation:none}}

/* Two up once there is room for two full addresses side by side. Below this
   they stay stacked — a half-width card truncates the address, which is the
   one thing on it that must stay whole. */
@media (min-width:760px){
  .cards{display:grid;grid-template-columns:1fr 1fr;align-items:start}
}

/* ============================================== the phone, specifically ====
 * The console is opened at a venue on a phone as often as at a desk, and the
 * header was built for the desk: a row of tabs, an email address and a sign
 * out button, all on one line. At 390px that line wraps into four.
 */
@media (max-width:700px){
  .wrap{
    padding:14px 14px 32px;
    /* A home-screen PWA has no browser chrome, so the page runs into the
       gesture bar. Without this the last row of the log is unreadable. */
    padding-bottom:calc(32px + env(safe-area-inset-bottom))
  }
  /* `.tabs` is nested INSIDE `.who`, not a child of `header` — so grid rules
     aimed at it from here never applied and the first attempt at this header
     rendered as a stack of boxes. The row that has to break is `.who`. */
  header{
    display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px;
    padding-bottom:12px;margin-bottom:16px
  }
  header>div:first-child{flex:1 1 auto;min-width:0}
  /* "eb-mail" broke across two lines at 390px. It is a name; it does not wrap. */
  h1{font-size:16px;white-space:nowrap}
  .sub{display:none}          /* the tagline is for a first visit, not a phone */
  /* `display:contents` dissolves `.who` so its children become direct flex
     children of `header` and can be placed independently. Sharing one row
     between the tabs and Sign out truncated the last tab to a single letter
     ("Audit" rendered as "A"); the tabs need the full width. */
  .who{display:contents}
  .who .muted{display:none}   /* one operator, and the address is 24 characters */
  #out{order:1;flex:0 0 auto;min-height:38px}
  .tabs{
    order:2;flex:1 1 100%;display:flex;gap:6px;min-width:0;
    overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;
    /* Bleed to the edges so a scrolled tab row does not look inset and stuck. */
    margin:0 -14px;padding:2px 14px
  }
  .tabs::-webkit-scrollbar{display:none}
  .tab{flex:0 0 auto;min-height:38px;padding:0 13px;white-space:nowrap}

  /* The search is the front door on a phone too, so it stays full width and
     the button sits under it rather than squeezing the input to nothing. */
  .search{flex-direction:column;align-items:stretch}
  .search input{width:100%}
  .search button{width:100%;min-height:44px}

  /* 16px or iOS zooms the whole page on focus and never zooms back. */
  input,select,button{font-size:16px}
  .filters{flex-direction:column;align-items:stretch;gap:8px}
  .filters>*{width:100%}
  .health{flex-direction:column;align-items:flex-start;gap:6px}
}

/* A dormant address gets height proportional to how much it has to say. */
.card.quiet{padding-bottom:10px;background:#fcfcfd}
.card.quiet .card-head{padding-bottom:12px}
.nohist{padding:2px 16px 0;margin:0}

/* An expandable card has to look expandable. A chevron that turns is the
   cheapest possible signal and needs no label in any language. */
.chev{
  margin-left:auto;flex:0 0 auto;width:8px;height:8px;border-right:2px solid var(--mut);
  border-bottom:2px solid var(--mut);transform:rotate(45deg);transition:transform .15s
}
.card-head[aria-expanded="true"] .chev{transform:rotate(-135deg)}
@media (prefers-reduced-motion:reduce){.chev{transition:none}}

.daily{display:block;width:100%;height:30px}

/* An explanation, not a warning: quieter than .alerts on purpose. */
.renamed{
  margin:0 0 12px;padding:10px 12px;font-size:12.5px;line-height:1.5;
  background:#f4f7fb;border-left:3px solid #cfd9e6;border-radius:0 10px 10px 0;color:var(--ink-2)
}

/* ======================================== tables become cards on a phone ====
 * The Mail, Keys and Audit tabs were still five-column tables. On a 390px
 * screen the log showed only "When" and "To" — the OUTCOME column, the single
 * thing somebody opens that tab to read, sat off the right edge behind an
 * overlay scrollbar that macOS draws invisible. It measured as 300px of
 * in-wrapper overflow and 86px-tall rows: the browser was starving every
 * column to fit, and the answer was simply not on screen.
 *
 * Scrolling sideways is not the fix. Each row becomes a card, each cell a
 * labelled line — the same shape the Addresses tab already uses, so the whole
 * console reads one way rather than two.
 */
/* `overflow-wrap:anywhere` on a table cell was tried here first and made
   things far worse. It does let a cell shrink below its longest token — but in
   a five-column table at 816px the recipient column had already been starved
   to about two characters, so "anywhere" obligingly wrapped an email address
   ONE LETTER PER LINE and turned every row 231px tall. Shrinking a column that
   is already too narrow is not a fix. The cards below are.
*/
/* Cards all the way up to the LAPTOP breakpoint, not just the phone.
 *
 * 641-1023px is a windowed browser on a MacBook Air — the single most common
 * real width, and the one a 390/1280 sweep skips entirely. At 816 the table
 * did not overflow, so every overflow probe called it healthy; it CRUSHED
 * instead, starving the recipient column until addresses wrapped vertically.
 * Five columns do not fit in 780px of content width, and no amount of wrapping
 * makes them, so the same cards the phone uses run to 1023px too. */
@media (max-width:1023px){
  .scroll{overflow-x:visible}
  .scroll table,
  .scroll tbody,
  .scroll tr,
  /* `min-width:660px` on the base table rule is what actually held the layout
     open. Switching the display types alone left it in place, so the table
     stayed 660px wide inside a 360px wrapper and every card overflowed by
     exactly the same 300px as before the rewrite — a change that measured as
     no change at all. Reset it explicitly. */
  .scroll td{display:block;width:auto;min-width:0}
  .scroll table{min-width:0;max-width:100%}
  /* The header is what the per-cell data-label replaces. */
  .scroll thead{display:none}

  .scroll tbody tr{
    display:flex;flex-direction:column;
    background:var(--panel);border:1px solid var(--line);border-radius:12px;
    box-shadow:var(--shadow);margin-bottom:10px;padding:4px 2px
  }
  .scroll tbody td{
    display:flex;align-items:baseline;justify-content:space-between;gap:14px;
    padding:7px 13px;border:0;text-align:right
  }
  .scroll tbody td + td{border-top:1px solid var(--line)}
  /* The column name, carried by the cell itself. */
  .scroll tbody td::before{
    content:attr(data-label);flex:0 0 auto;text-align:left;
    font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
    color:var(--mut)
  }
  /* An emptied action cell would otherwise draw a bare divider. */
  .scroll tbody td:empty{display:none}
  .scroll tbody td.actions{justify-content:flex-end}
  .scroll tbody td.actions::before{content:none}
  /* Long values wrap instead of forcing the card wide. `anywhere` so the cell
     can shrink below its longest token, which is what removes the overflow. */
  .scroll tbody td.mono{overflow-wrap:anywhere;text-align:right}

  /* Read the answer first. The DOM order is When/To/Type/Outcome/Application,
     which is the right order for a table scanned by column and the wrong one
     for a card read top-down: on a phone the questions are "who" and "did it
     work", and the timestamp only matters once those are answered. */
  #log tr.logrow td[data-label="To"]{order:1}
  #log tr.logrow td[data-label="Outcome"]{order:2}
  #log tr.logrow td[data-label="When"]{order:3}
  /* These two need an explicit order even though they are hidden on a phone.
     Without one they default to 0 and sort BEFORE everything given an order —
     so at tablet width the card led with "Type" and "Application" and buried
     the recipient and the outcome three lines down. */
  #log tr.logrow td[data-label="Type"]{order:4}
  #log tr.logrow td[data-label="Application"]{order:5}


  /* A full-width row that is not a record: no card chrome, no label. */
  .scroll tbody td.empty{text-align:center;padding:22px 12px}
  .scroll tbody td.empty::before{content:none}
  .scroll tbody tr:has(td.empty){background:none;border:0;box-shadow:none}

  /* The expanded detail row is already a definition grid; it just needs to
     stop pretending to be a table cell. */
  .scroll tbody tr.detail{padding:0}
  .scroll tbody tr.detail td{padding:12px 13px;text-align:left}
  .scroll tbody tr.detail td::before{content:none}

}

/* Two cards abreast once there is room for two full email addresses, so the
   tablet and small-laptop widths are not a single narrow column of cards with
   half the screen empty. */
@media (min-width:701px) and (max-width:1023px){
  .scroll tbody{display:grid;grid-template-columns:1fr 1fr;gap:0 10px;align-items:start}
  /* An expanded detail belongs to the row above it, so it spans both. */
  .scroll tbody tr.detail{grid-column:1 / -1}
}

@media (max-width:700px){
  /* Dates side by side — two full-width pickers stacked pushed the table an
     entire screen down for a filter most visits never touch. */
  .filters{flex-direction:row;flex-wrap:wrap;align-items:center;gap:8px}
  .filters>*{width:auto}
  .filters #f-stream{flex:1 1 100%}
  .filters #f-from,.filters #f-to{flex:1 1 0;min-width:0}
  .filters label.inline{flex:1 1 100%}
  .bar{flex-direction:column;align-items:stretch;gap:8px}
}

/* Three lines, not five — PHONE ONLY. The row expands on tap and both fields
   are in that detail, so carrying them on the collapsed card turned seventeen
   entries into an eight-screen scroll. At tablet width there is room, so they
   stay. */
@media (max-width:700px){
  #log tr.logrow td[data-label="Type"],
  #log tr.logrow td[data-label="Application"]{display:none}
}

.qsplit{margin-top:6px;line-height:1.55}
.qsplit code{font-size:11px}
.qsplit .hist{color:var(--mut)}

/* Historical identities: present, findable, and out of the way. */
.hist-wrap{margin-top:16px}
.hist-wrap summary{
  cursor:pointer;padding:11px 14px;border:1px solid var(--line);border-radius:12px;
  background:#fcfcfd;font-size:13px;line-height:1.5;list-style:none
}
.hist-wrap summary::-webkit-details-marker{display:none}
.hist-wrap summary::before{content:'\25B8 ';color:var(--mut)}
.hist-wrap[open] summary::before{content:'\25BE '}
.hist-wrap summary:hover{background:#f7f7f9}
