/* =====================================================================
   The respawn dashboard, as the bot serves it at /dashboard.

   Ported from tibia-bot/src/main/resources/web/board.html. That file
   opens with the same tokens this site does — --bg, --panel,
   --panel-border, --accent and the rest are identical, deliberately — so
   unlike the Discord skin this needs no palette of its own and no font.
   Only the state colours below are new here, and they are copied across
   verbatim.

   Everything is scoped under .db. Several of board.html's class names
   (.card, .panel, .dot, .who) are generic enough to collide with a page
   that grows, and a collision of exactly that kind already cost us once.
   ===================================================================== */

.db {
  /* board.html :root, the five state colours plus the two ink colours a
     spawn card is drawn in. */
  --st-free: #2ecc71;
  --st-claimed: #d23369;
  --st-booked: #5b8cff;
  --st-confirmed: #5b8cff;
  --st-asked: #e0a93e;
  --code: #f0b232;
  --spawn-name: #e8e9ea;
  --claimant-rgb: 130, 140, 245;
  --claimant: rgb(var(--claimant-rgb));
}

/* ---------------------------------------------------------- masthead */
.db-masthead {
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--panel-border); border-radius: 8px;
  padding: 11px 15px; margin-bottom: 12px;
  display: flex; align-items: center; gap: 13px; flex-wrap: wrap;
}
.db-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.db-brand img { width: 34px; height: 34px; border-radius: 8px; flex: none; }
.db-brand .server {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text-strong); font-weight: 600; font-size: 14px;
}
.db-brand .server .ic { color: var(--muted); }
.db-brand .path { color: var(--muted); font-weight: 400; }
.db-brand .sub {
  display: block; font-size: 10px; font-weight: 400; color: var(--muted);
  letter-spacing: 0.04em; margin-top: 2px; font-family: var(--mono);
}
.db-tier {
  font-family: var(--mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.08em; border-radius: 4px; padding: 2px 7px;
  background: rgba(167, 139, 250, 0.16); color: var(--purple); flex: none;
}
.db-filter {
  margin-left: auto; flex: 0 1 240px; min-width: 0;
  background: rgba(0, 0, 0, 0.25); border: 1px solid var(--panel-border);
  border-radius: 7px; padding: 7px 11px; color: var(--text);
  font: inherit; font-size: 12px;
}
.db-filter::placeholder { color: var(--muted); }
.db-filter:focus { outline: none; border-color: var(--accent); }

/* ------------------------------------------------- panels and titles */
.db-panel {
  border: 1px solid var(--panel-border); border-radius: 8px;
  background: var(--panel); margin-bottom: 12px; overflow: hidden;
}
.db-panel-title {
  padding: 11px 15px; font-family: var(--mono); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted);
  border-bottom: 1px solid var(--panel-border);
  display: flex; align-items: center; gap: 8px;
}
.db-panel-title::before { content: '##'; color: var(--accent); letter-spacing: normal; }

/* Moderator tools: shut until asked for, and above the list because what
   it does — adding a code — changes the list underneath it. */
.db-fold {
  width: 100%; display: flex; align-items: center; gap: 9px;
  padding: 11px 15px; background: none; border: 0; color: var(--text-strong);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; text-align: left;
}
.db-fold:hover { background: rgba(255, 255, 255, 0.02); }
.db-fold .chev { margin-left: auto; color: var(--muted); font-size: 13px; transition: transform 0.15s ease; }
.db-panel.open .db-fold .chev { transform: rotate(180deg); }
.db-fold-body { display: none; padding: 0 15px 14px; }
.db-panel.open .db-fold-body { display: block; }
.db-fold-body p { color: var(--muted); font-size: 12.5px; margin: 0 0 10px; }

/* ------------------------------------------------------------- cards */
.db-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
  gap: 10px; padding: 13px 15px;
}
.db-card {
  position: relative; overflow: hidden; cursor: pointer; text-align: left;
  background: rgba(255, 255, 255, 0.02); border: 1px solid var(--panel-border);
  border-radius: 8px; padding: 10px 12px; font: inherit; color: inherit;
}
.db-card:hover { border-color: var(--accent); }
/* State also shows as a hairline down the leading edge, because on a busy
   watermark the dot alone gets lost against the art. */
.db-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0;
  width: 2px; background: var(--edge, transparent);
}
/* 128px is exactly twice a sprite's own 64px canvas, so every source pixel
   lands on a whole number of device pixels and the art stays hard-edged.
   Bigger than the card on purpose: the card is a window onto the art
   rather than a frame around it. Never mirrored — Tibia sprites face a
   deliberate direction. */
.db-card .sprite {
  position: absolute; right: -6px; top: 0; bottom: 0; margin: auto;
  width: 128px; height: 128px; opacity: 0.17; pointer-events: none;
  image-rendering: pixelated; object-fit: contain;
}
.db-card .body { position: relative; }
.db-card .top { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }

.db .code-chip {
  display: inline-flex; align-items: center; font-family: var(--mono); font-weight: 700;
  font-size: 12px; font-variant-numeric: tabular-nums; color: var(--code);
  background: rgba(240, 178, 50, 0.13); border: 1px solid rgba(240, 178, 50, 0.32);
  border-radius: 5px; padding: 2px 7px; flex: none;
}
/* A spawn being hunted right now is the one thing on the board about to
   change, so its code is the one thing that moves. */
.db .code-chip.live { color: #ffd98a; border-color: rgba(240, 178, 50, 0.75); animation: db-pulse 2.6s ease-out infinite; }
@keyframes db-pulse {
  0% { box-shadow: 0 0 0 0 rgba(240, 178, 50, 0.42); }
  70% { box-shadow: 0 0 0 6px rgba(240, 178, 50, 0); }
  100% { box-shadow: 0 0 0 0 rgba(240, 178, 50, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .db .code-chip.live { animation: none; box-shadow: 0 0 0 2px rgba(240, 178, 50, 0.3); }
}

.db .spawn-name {
  color: var(--spawn-name); font-size: 12.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.db .card-state { display: flex; align-items: center; gap: 6px; font-size: 11.5px; }
.db .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; border: 1.5px solid; }
.db .dot.free { border-color: var(--st-free); background: var(--st-free); }
.db .dot.claimed { border-color: var(--st-claimed); background: var(--st-claimed); }
.db .dot.booked { border-color: var(--st-booked); background: transparent; }
.db .dot.confirmed { border-color: var(--st-confirmed); background: var(--st-confirmed); }
.db .dot.asked { border-color: var(--st-asked); background: var(--st-asked); }
.db .who {
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  display: flex; align-items: center; gap: 4px; min-width: 0;
}
.db .who.free { color: var(--st-free); }
/* A state line is mostly punctuation and clock times, and the one word in
   it worth finding at a glance is the person — so it is the one word set
   apart, by weight AND colour. At 11px monospace in the muted grey, bold
   alone is a difference you have to be told about before you can see it. */
.db .who .at-name {
  color: var(--claimant); background: rgba(var(--claimant-rgb), 0.12);
  border-radius: 4px; padding: 1px 5px; font-weight: 700; flex: none;
}
.db .who .tick { color: var(--st-confirmed); font-size: 11px; flex: none; }
.db .queue { font-family: var(--mono); font-size: 10px; color: var(--muted); flex: none; margin-left: auto; }

.db .card-bar {
  height: 3px; border-radius: 2px; background: rgba(255, 255, 255, 0.07);
  margin-top: 8px; overflow: hidden;
}
.db .card-bar span {
  display: block; height: 100%; border-radius: 2px;
  background: var(--st-claimed); transition: width 1s linear;
}

.db-empty { padding: 26px 16px; color: var(--muted); font-size: 13px; text-align: center; }

/* ------------------------------------------------- the spawn window */
.db-veil {
  position: fixed; inset: 0; background: rgba(4, 5, 8, 0.72);
  backdrop-filter: blur(2px); z-index: 40; display: none;
}
.db-veil.open { display: block; }
/* board.html's .window, verbatim. It is a panel rather than a dialog —
   the grid is the thing it exists for, and a week squeezed into a small
   modal is a week nobody can read. */
.db-window {
  position: fixed; z-index: 41; display: none;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(1600px, calc(100vw - 28px));
  height: auto; max-height: min(1180px, calc(100vh - 40px));
  background: var(--panel); border: 1px solid #2a3142; border-radius: 14px;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  /* Nothing inside hands its leftover scroll to the page behind it. */
  overscroll-behavior: contain;
}
.db-window.open { display: flex; flex-direction: column; }
/* board.html's watermark: the whole panel, not a corner. object-position
   sits it just above centre so the head of a creature lands behind the
   grid rather than behind the footer. */
.db-window .watermark {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; object-position: center 46%;
  image-rendering: pixelated; pointer-events: none; user-select: none;
  opacity: 0.09; filter: saturate(0.85);
}
/* What keeps the art from reading as a picture behind a calendar. board.html
   lays this over the watermark: a dark radial that is nearly clear at the
   middle and closes in towards the edges, so the sprite surfaces in the
   centre of the window and sinks everywhere else. The window's own head and
   body are positioned, so they paint above it and stay crisp. */
.db-window .scrim {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 45%, rgba(18, 21, 28, 0.12) 0%, rgba(18, 21, 28, 0.7) 62%, rgba(18, 21, 28, 0.93) 100%),
    linear-gradient(rgba(18, 21, 28, 0.55), rgba(18, 21, 28, 0.18) 30%, rgba(18, 21, 28, 0.75));
}
.db-window .win-head {
  position: relative; display: flex; align-items: center; gap: 9px;
  padding: 13px 15px; border-bottom: 1px solid var(--panel-border);
}
.db-window .win-title {
  color: var(--text-strong); font-size: 14px; font-weight: 600; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.db-window .win-region { color: var(--muted); font-size: 11px; font-family: var(--mono); flex: none; }
.db-window .win-close {
  margin-left: auto; background: none; border: 0; color: var(--muted);
  font-size: 20px; line-height: 1; cursor: pointer; padding: 0 2px; flex: none;
}
.db-window .win-close:hover { color: var(--text-strong); }
/* A column, so the calendar inside it can be told to take what is left.
   min-height:0 because a flex item's floor is its content, and without it
   the grid would push the window taller rather than scroll inside it. */
.db-window .win-body {
  position: relative; padding: 0; flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: #2a3140 transparent;
}
.db .cal { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.db-window .win-body::-webkit-scrollbar { width: 10px; }
.db-window .win-body::-webkit-scrollbar-track { background: transparent; }
.db-window .win-body::-webkit-scrollbar-thumb {
  background: #2a3140; border-radius: 5px;
  border: 3px solid var(--panel); background-clip: padding-box;
}
.db-window .win-foot { position: relative; flex-shrink: 0; }

/* The readout: the length on the left, the one action on the right. Its
   height never changes, so the button somebody presses over and over
   stays in one place however much the panel has to say. */
.db .readout {
  padding: 10px 20px; border-top: 1px solid var(--panel-border);
  display: flex; align-items: center; gap: 12px; font-size: 12.5px;
}
.db .foot-dur { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; min-width: 0; }
.db .readout-actions { flex-shrink: 0; display: flex; align-items: center; gap: 10px; margin-left: auto; }

/* How long the hunt would run. Grey is what today's stamina has already
   gone on, blue is what this hunt would take, and the white edge is where
   it would end. */
.db .tank {
  display: block; position: relative; width: 240px; height: 34px; border-radius: 8px;
  border: 1px solid var(--panel-border); background: rgba(255, 255, 255, 0.02);
  overflow: hidden; touch-action: none; user-select: none; cursor: ew-resize;
}
.db .tank-zones {
  position: absolute; inset: 5px; border-radius: 4px;
  background: rgba(255, 255, 255, 0.05); overflow: hidden; display: flex;
}
.db .tank-hunt { background: var(--accent); }
.db .tank-edge {
  position: absolute; top: 3px; bottom: 3px; width: 3px; border-radius: 2px;
  background: var(--text-strong); box-shadow: 0 0 6px rgba(0, 0, 0, 0.7);
  z-index: 2; pointer-events: none;
}
.db .tank-in {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  white-space: nowrap; pointer-events: none;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
}
.db .tank-in { font-size: 12.5px; font-weight: 600; color: #06122e; }

.db .btn {
  background: var(--accent); border: 1px solid var(--accent); border-radius: 6px;
  color: var(--bg); font-size: 12.5px; font-weight: 600; padding: 6px 15px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
}
.db .btn.claim { background: var(--st-free); border-color: var(--st-free); color: #06301a; }
.db .btn i { font-size: 15px; }

/* ===================================================================
   The week, from board.html.

   Two grids of identical shape stacked: .wk-headrow for the day headings
   and .wk-rows for the columns. Everything absolutely placed on the week
   — the now line, server save — belongs to the lower one, so its
   coordinates stay measured from midnight and owe nothing to how tall the
   headings happen to be.
   =================================================================== */

/* A row of fixed text on a fixed row, as board.html's .strip. */
.db .strip {
  display: flex; align-items: center; gap: 11px; padding: 9px 15px; flex-wrap: wrap;
  border-bottom: 1px solid var(--panel-border); font-size: 12px; color: var(--muted);
}
.db .strip .fold-title { margin-left: auto; color: var(--text-strong); font-weight: 600; font-size: 12.5px; }
.db .strip .muted { color: var(--muted); font-size: 11.5px; }

.db .cal-controls { gap: 10px 14px; }
.db .cc-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.db .cc-week { margin-left: auto; gap: 10px; }
.db .field-label { font-size: 11px; color: var(--muted); }
.db .seg { display: inline-flex; border: 1px solid var(--panel-border); border-radius: 7px; overflow: hidden; }
.db .seg button {
  background: none; border: 0; color: var(--muted); font: inherit; font-size: 11.5px;
  font-weight: 600; padding: 5px 11px; cursor: pointer; font-family: var(--mono);
}
.db .seg button + button { border-left: 1px solid var(--panel-border); }
.db .seg button.on { background: var(--accent); color: #0b0d12; }
.db .btn-ghost {
  display: inline-flex; align-items: center; gap: 5px;
  background: none; border: 1px solid var(--panel-border); border-radius: 7px;
  padding: 5px 10px; color: var(--muted); font: inherit; font-size: 11.5px; cursor: pointer;
}

/* Fixed text on a fixed row, written once. */
.db .cal-legend {
  display: flex; gap: 14px; padding: 9px 15px; flex-wrap: wrap;
  border-bottom: 1px solid var(--panel-border);
}
.db .cal-legend span { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); }
.db .cal-legend .lg-end { margin-left: auto; }
.db .swatch { width: 11px; height: 11px; border-radius: 3px; flex: none; border: 1.5px solid; }
.db .sw-claimed { border-color: var(--st-claimed); background: rgba(210, 51, 105, 0.3); }
.db .sw-booked { border-color: var(--st-booked); background: transparent; border-style: dashed; }
.db .sw-confirmed { border-color: var(--st-confirmed); background: var(--st-confirmed); }
.db .sw-asked { border-color: var(--st-asked); background: rgba(224, 169, 62, 0.28); }
.db .sw-pick { border-color: var(--purple); border-style: dashed; background: rgba(167, 139, 250, 0.4); }
.db .line-now { border: none; border-left: 2px solid var(--bad); border-radius: 0; width: 2px; }
.db .line-ss { border: none; border-left: 2px dashed var(--warn); border-radius: 0; width: 2px; }

.db .wk-body {
  overflow: auto; max-height: calc(100vh - 340px); min-height: 260px; touch-action: pan-x pan-y;
  scrollbar-width: thin; scrollbar-color: #2a3140 transparent;
}
.db .wk-body::-webkit-scrollbar { width: 10px; height: 10px; }
.db .wk-body::-webkit-scrollbar-track { background: transparent; }
.db .wk-body::-webkit-scrollbar-thumb {
  background: #2a3140; border-radius: 5px;
  border: 3px solid var(--panel); background-clip: padding-box;
}
.db .wk-grid {
  position: relative; width: max-content;
  --gutter: 56px; --daycol: 150px; --row: 28px; --min-block: 13px;
}
.db .wk-headrow,
.db .wk-rows { display: grid; grid-template-columns: var(--gutter) repeat(15, var(--daycol)); }
.db .wk-headrow { position: sticky; top: 0; z-index: 7; background: var(--panel); }
.db .wk-rows { position: relative; }

/* The corner the two sticky axes meet in, above both of them. */
.db .wk-corner {
  position: sticky; left: 0; z-index: 8; background: var(--panel);
  border-bottom: 1px solid var(--panel-border);
}
.db .wk-head {
  font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--muted); text-align: center; padding: 8px 0 7px;
  border-bottom: 1px solid var(--panel-border);
}
.db .wk-head.today { color: var(--accent); }
.db .wk-head .dnum { display: block; font-size: 13px; color: var(--text-strong); margin-top: 2px; letter-spacing: 0; }

/* Stuck to the left edge with the days sliding behind it. It needs its own
   background for that, or the columns show through the hour labels. */
.db .wk-hours {
  border-right: 1px solid var(--panel-border);
  position: sticky; left: 0; z-index: 5; background: var(--panel);
}
.db .wk-hour {
  height: var(--row); font-family: var(--mono); font-size: 9.5px; color: var(--muted);
  text-align: right; padding-right: 7px; position: relative; top: -5px;
  font-variant-numeric: tabular-nums;
}
/* The same local hour said twice, twelve-hour over twenty-four: which of
   the two ways of writing it a reader thinks in is the thing that
   actually varies between people. */
.db .wk-hour .srv { display: block; font-size: 8px; opacity: 0.5; margin-top: -3px; }

.db .wk-col { border-left: 1px solid var(--panel-border); position: relative; }
.db .wk-cell { height: var(--row); border-bottom: 1px solid rgba(31, 36, 48, 0.55); }
.db .wk-cell.past { background: rgba(0, 0, 0, 0.3); }

.db .wk-block {
  position: absolute; left: 2px; right: 2px; border-radius: 4px; padding: 2px 5px;
  overflow: hidden; font-family: var(--mono); font-size: 9.5px; line-height: 1.25; border: 1px solid;
  top: calc(var(--row) * var(--at, 0));
  height: max(var(--min-block), calc(var(--row) * var(--len, 1) - 2px));
  /* Opaque, whatever the state tint. A wash with the hour rules showing
     through reads as coloured glass laid over the grid rather than as a
     thing sitting on it — and on a two-hour block the line across the
     middle looks like the block has been cut in two. */
  background: linear-gradient(var(--tint, transparent), var(--tint, transparent)), var(--panel);
}
.db .wk-block .who { font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A time never breaks across two lines. Half a range on one line and half
   on the next is not a shorter label, it is a wrong-looking one. */
.db .wk-block .tm { opacity: 0.75; font-variant-numeric: tabular-nums; display: block; white-space: nowrap; }

/* State decides the colour; whose it is decides how loud. Two hues would
   have meant a booking of yours and one of theirs looked like different
   kinds of thing, which they are not. */
.db .b-claimed { --tint: rgba(210, 51, 105, 0.26); border-color: var(--st-claimed); color: #f3a6c0; }
.db .b-booked { --tint: rgba(91, 140, 255, 0.16); border-color: var(--st-booked); border-style: dashed; color: #cfdcff; }
.db .b-confirmed { --tint: rgba(91, 140, 255, 0.3); border-color: var(--st-confirmed); color: #cfdcff; }
.db .b-asked { --tint: rgba(224, 169, 62, 0.2); border-color: var(--st-asked); color: #e8c887; }
.db .b-mine { box-shadow: inset 3px 0 0 currentColor; }
/* A block cut by local midnight keeps a flat, dashed edge, so it reads as
   continuing into the next column rather than as a hunt that really stops
   at the bottom of the day. */
.db .clip-top { border-top-left-radius: 0; border-top-right-radius: 0; border-top-style: dashed; }
.db .clip-bottom { border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom-style: dashed; }

/* Server save and now. Both mark a height on the hour axis, and that axis
   is shared by every column — which is why each spans the whole grid, and
   why the red flag labelling one lives in the gutter, on screen whatever
   the strip has been scrolled to. */
.db .ss-line {
  position: absolute; left: 0; right: 0; height: 0; z-index: 2; pointer-events: none;
  border-top: 1px dashed rgba(224, 169, 62, 0.45); top: calc(var(--row) * var(--at, 0));
}
.db .ss-label {
  position: absolute; right: 3px; top: calc(var(--row) * var(--at, 0)); transform: translateY(-100%);
  font-family: var(--mono); font-size: 8px; color: var(--warn); opacity: 0.9;
  letter-spacing: 0.08em; pointer-events: none; z-index: 3;
}
.db .now-line {
  position: absolute; left: 0; right: 0; height: 0; z-index: 4; pointer-events: none;
  border-top: 1px solid var(--bad); top: calc(var(--row) * var(--at, 0));
}
.db .now-flag {
  position: absolute; left: 2px; top: calc(var(--row) * var(--at, 0));
  transform: translateY(-50%); z-index: 6; pointer-events: none;
  font-family: var(--mono); font-size: 8.5px; font-weight: 600; color: var(--bg);
  background: var(--bad); border-radius: 3px; padding: 1px 3px;
}

/* The Discord mark before a claimant name. */
.db .who .d-mark { width: 11px; height: 11px; flex: none; vertical-align: -1px; fill: var(--claimant); }

@media (max-width: 680px) {
  /* dvh, not vh: on a phone 100vh is the viewport with the browser's own
     chrome pretended away, so a full-height panel is taller than the
     screen actually showing it. A phone wants the whole screen — this is
     the one case where filling it is the point rather than fitting. */
  .db-window { width: 100vw; height: 100vh; height: 100dvh; max-height: none; border-radius: 0; }
  .db-filter { margin-left: 0; flex: 1 1 100%; }
  .db-cards { grid-template-columns: 1fr; padding: 11px 12px; }

  /* Nothing outside the week scrolls: it is the part that is too big for a
     phone, and everything else earns its place by being small enough. The
     grid takes the rest of the window rather than a fixed slice of it. */
  .db-window .win-body { overflow-y: hidden; }
  .db .wk-body { flex: 1 1 auto; max-height: none; min-height: 120px; margin: 0 3px; }

  /* Tighter, so the grid keeps as much of a short screen as it can. Every
     one of these is padding: the rows still say everything they said, they
     simply stop holding it at arm's length. */
  .db-window .win-head { padding: 7px 12px 6px; }
  .db .strip { padding: 6px 12px; }
  .db .readout { padding: 6px 12px; gap: 10px; }
  /* "Book a time", which on a phone is a heading over a row of controls
     that already say what they are for. What is left of the row — whose
     slot it is, and until when — is the only place a phone says it. */
  .db .cal-head .fold-title { display: none; }
  /* Seven keys to a grid whose colours are its own legend, across a screen
     with no width to spare for them. */
  .db .cal-legend { display: none; }

  /* The day and its number on one line. Two lines of heading over four
     columns is a row of the grid spent saying what the row below shows. */
  .db .wk-head { padding: 3px 0 2px; letter-spacing: 0.06em; }
  .db .wk-head .dnum { display: inline; margin: 0 0 0 4px; font-size: 11.5px; }

  /* ---- Four days, not fifteen -----------------------------------------
     A fifteenth of a phone is about twenty pixels, which cannot say who has
     a slot or when it runs. So the columns keep a width worth reading and
     the week slides sideways for the rest of itself, opened on yesterday so
     today and the two days worth planning are already in view. The gutter
     and the day headings stay put while it does. */
  .db .wk-grid {
    /* The widest thing in the gutter is the server echo at 22px, so forty
       pixels carried empty space to the left of every label. */
    --gutter: 30px;
    /* The four that fit: the window, less the body's margins, less the
       gutter, split four ways. */
    --daycol: calc((100vw - 6px - 30px) / 4);
  }
  /* Every pixel of a narrow column goes to the label, so a whole time fits
     on one line and the name has somewhere to be above it. The size follows
     the screen, because a quarter of a 320px phone is not a quarter of a
     430px one and "19:00-21:00" has to survive both — clamped at the top so
     a big phone gets the desktop size rather than an inflated one. */
  .db .wk-block { padding: 2px 4px; font-size: clamp(8px, 2.4vw, 9.5px); }
  /* The label sits closer to its own edge, the gutter having none to spare. */
  .db .wk-hour { padding-right: 5px; }

  /* Booking controls on one row: stacked and centred they took three rows
     of a short screen. The week nav keeps its chevrons and drops its words. */
  .db .cal-controls { justify-content: center; gap: 8px; }
  .db .cc-repeat .field-label { display: none; }
  .db .cc-week { margin-left: 0; gap: 8px; }
  .db .cc-week .btn-ghost span { display: none; }
  .db .cc-week .btn-ghost { padding: 5px 8px; }

  /* The tank takes what is left of the foot beside the button it feeds. */
  .db .foot-dur { flex: 1 1 auto; }
  .db .foot-dur .tank { width: auto; flex: 1; height: 40px; }
  .db .tank-in { font-size: 13px; }
}
