/* The ally-code bar. Everything else on this page is styled by
   journey-guide.css, lifted from swgoh_graphic — this is the one control that
   report has no equivalent for, so it is styled to match rather than copied.

   Type here is already at the scaled size journey-guide.css is built to (see
   FONT_SCALE in scripts/build_journey_guides.py). This sheet is hand-written,
   so write sizes at that scale rather than re-running the transform, which
   would compound. */

.jg-ally {
  display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 14px;
}
.jg-ally label {
  font-family: var(--mono); font-size: 12.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--label);
}
.jg-ally input {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  font-family: var(--mono); font-size: 15.5px; letter-spacing: 0.06em;
  padding: 7px 11px; border-radius: 8px; width: 140px;
}
.jg-ally input:focus { outline: none; border-color: var(--gold); }
.jg-ally button {
  font-family: var(--sans); font-size: 14.5px; font-weight: 700;
  padding: 7px 15px; border-radius: 8px; cursor: pointer;
  background: var(--gold); border: 1px solid var(--gold); color: #14161f;
}
.jg-ally button.ghost {
  background: transparent; border-color: var(--border); color: var(--text-muted);
}
.jg-ally button.ghost:hover { color: var(--text); border-color: var(--border); }
.jg-ally button:disabled { opacity: .5; cursor: default; }

.lookup-status { font-family: var(--mono); font-size: 14px; color: var(--text-muted); }
.lookup-status.ok { color: var(--status-met); }
.lookup-status.error { color: var(--status-pending); }
.lookup-status.busy { color: var(--group-ships); }

.jg-privacy { margin: 9px 0 0; font-size: 13px; color: var(--text-muted); opacity: .8; }

/* The site header sits above the report's own header; keep them from
   fighting over the page background. */
body { background: var(--bg); }

/* Signal Data icons. swgoh_graphic inlines these as data URIs generated by
   _signal_icon_css(); here they are real files under /assets/signal/, exported
   by the same build script that lifts this page's stylesheet. */
.jg-ic-fragmented { background-image: url("/assets/signal/fragmented.png"); }
.jg-ic-incomplete { background-image: url("/assets/signal/incomplete.png"); }
.jg-ic-flawed     { background-image: url("/assets/signal/flawed.png"); }
.jg-ic-corrupted  { background-image: url("/assets/signal/corrupted.png"); }
.jg-ic-lst        { background-image: url("/assets/signal/aurodium_lst.svg"); }
.jg-ic-aurodium   { background-image: url("/assets/signal/aurodium_lst.svg"); }
.jg-ic-kyber      { background-image: url("/assets/signal/kyber_lst.png"); }
.jg-ic-cantina    { background-image: url("/assets/signal/cantina_energy.png"); }

/* The farm inputs. swgoh_graphic reads these from the account's YAML, so it has
   no controls for them; these reuse its .jg-income / .jg-refresh styling. */
#jg-inputs { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
#jg-inputs input[type="number"] {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  font-family: var(--mono); font-size: 14px; padding: 3px 7px; border-radius: 6px;
}
#jg-inputs input[type="number"]:focus { outline: none; border-color: var(--gold); }
#jg-inputs input[type="checkbox"] { accent-color: var(--group-ships); cursor: pointer; margin: 0; }

/* The account-details section reuses swgoh_graphic's own collapsible treatment
   (.jg-working — mono uppercase summary with a rotating caret) rather than
   inventing a second one, with the footer-specific spacing removed since this
   one sits in the page header. */
/* Each collapsible is its own bordered panel, so the two read as distinct
   boxes rather than two rules across the header. Uses the same surface, border
   and radius as .jg-signal, which is the report's own panel treatment. The
   .jg-working base these inherit is styled as a footer rule, so its border-top,
   margin and padding are all replaced here. */
.jg-account {
  margin-top: 10px;
  padding: 9px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
}
.jg-account > summary {
  padding: 1px 0;
  color: var(--label);
}
.jg-account > summary:hover { color: var(--text); }
/* A little air between the heading and the controls, but only once open. */
.jg-account[open] { padding-bottom: 12px; }
.jg-account[open] > summary {
  margin-bottom: 9px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
  display: flex;
  width: 100%;
}

/* ---- header layout ----
   swgoh_graphic's report has no forms in its header, so its .jg-header just
   stacks. Here the legend and the ally-code form share one row, and the header
   is capped and centred to line up with the layout below it. */

/* One width for the header and the layout below it, widened on large
   displays. The lifted stylesheet caps .jg-layout at 1300px; this tracks it and
   overrides it together so the two never drift apart. */
:root { --page-width: 1300px; }
@media (min-width: 1500px) { :root { --page-width: 1440px; } }
@media (min-width: 1800px) { :root { --page-width: 1560px; } }

.jg-header { max-width: var(--page-width); margin: 0 auto; }
.jg-layout { max-width: var(--page-width); }

.jg-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 28px;
  margin-top: 12px;
}
/* The legend keeps the top margin the lifted stylesheet gives it only when it
   is on its own line; inside the controls row the row owns the spacing. */
.jg-controls .jg-legend { margin-top: 0; }
.jg-controls .jg-ally { margin-top: 0; }

@media (min-width: 900px) {
  /* Push the form to the right edge so the legend and the form bracket the
     row rather than bunching at the left. */
  .jg-controls .jg-ally { margin-left: auto; }
}

/* ---- the two input sections ----
   Two blocks per line once there is room, so the four in Your account details
   pair up as Coliseum + Assault battles, then Signal Data income + Cantina
   refreshes — which is also their source order. Below the breakpoint they
   stack. The lifted stylesheet spaces .jg-income for the report's stacked
   layout, so its margins are overridden here rather than in the generated
   file. */

#jg-inputs, #jg-inventory {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px 40px;
  align-items: baseline;
  padding-bottom: 2px;
}
#jg-inputs > .jg-income,
#jg-inputs > .jg-refresh,
#jg-inventory > .jg-income {
  margin-top: 0;
  gap: 6px 10px;
  min-width: 0;
}
/* Each block's label holds its line rather than collapsing when space is tight. */
#jg-inputs .jg-income-label,
#jg-inventory .jg-income-label { white-space: nowrap; }

/* The four Signal Data fields read as one unit, so they wrap together. */
#jg-inventory .jg-income-opt { gap: 4px; }
#jg-inventory input[type="number"] { width: 72px; }

@media (min-width: 860px) {
  #jg-inputs, #jg-inventory { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .jg-account > summary { margin-bottom: 2px; }
}

/* Completed journeys is not another category — it is where finished ones go —
   so it is set apart from the three live groups rather than continuing the
   list, and reads as muted until opened. */
.jg-nav-group--done {
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--border-soft);
  border-radius: 10px;
}
.jg-nav-group--done .jg-nav-group-label { color: var(--status-met); }
.jg-nav-group--done .jg-nav-item { color: var(--text-muted); opacity: .85; }
.jg-nav-group--done .jg-nav-item:hover { opacity: 1; }

/* ---- farming plan ----
   A third column beside the guide cards. swgoh_graphic's layout is nav + main,
   so the column and everything in it is defined here; the panel treatment
   matches .jg-signal so it belongs to the same design. */

/* The plan column. Not pinned and not scrollable: a panel capped at the
   viewport hides whatever does not fit, and the plan is exactly the thing you
   want to read all of — the same trap the nav fell into. The page scrolls
   instead, and the column is wide enough for a row's chips, date and controls
   to sit on one line, growing with the page on large displays. */
.jg-plan {
  flex: 0 0 clamp(268px, 21vw, 330px);
  align-self: flex-start;
  min-width: 0;
  /* Narrow enough that a row's contents have to wrap. They must wrap rather
     than push the column wide, so nothing inside is allowed to set its own
     width past the column's — see the rules below. */
  overflow-x: hidden;
  padding: 16px 12px 24px 0;
}

.jg-plan-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.jg-plan-head h2 { margin: 0; font-size: 17px; font-weight: 700; }
.jg-plan-count {
  font-family: var(--mono); font-size: 13px; color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.jg-plan-empty { margin: 0; font-size: 14.5px; color: var(--text-muted); line-height: 1.55; }

.jg-plan-list { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; }

.jg-plan-item {
  display: flex; align-items: flex-start; gap: 9px;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 9px; padding: 8px 9px;
}
.jg-plan-step {
  flex: 0 0 auto; width: 19px; height: 19px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border);
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  display: grid; place-items: center; color: var(--text-muted);
}
.jg-plan-body { flex: 1; min-width: 0; }
.jg-plan-name { display: block; font-size: 14.5px; font-weight: 700; }
.jg-plan-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 3px;
}
.jg-plan-meta .jg-signal-chip { font-size: 12.5px; }
.jg-plan-meta .jg-signal-icon { width: 14px; height: 14px; }
.jg-plan-meta .jg-time-days { font-size: 12.5px; margin-left: auto; }
/* A long journey name is broken rather than allowed to widen the column. */
.jg-plan-name { overflow-wrap: anywhere; }

.jg-plan-actions { display: flex; flex-direction: column; gap: 2px; flex: 0 0 auto; }
.jg-plan-actions button {
  background: none; border: 1px solid transparent; color: var(--text-muted);
  font: inherit; font-size: 11.5px; line-height: 1; padding: 2px 4px;
  border-radius: 4px; cursor: pointer;
}
.jg-plan-actions button:hover:not(:disabled) { color: var(--text); border-color: var(--border); }
.jg-plan-actions button:disabled { opacity: .25; cursor: default; }

.jg-plan-total {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 10px; padding: 11px 13px;
  display: flex; flex-direction: column; gap: 8px;
}
.jg-plan-total .jg-signal-line { gap: 6px 10px; }
.jg-plan-time { border-top: 1px solid var(--border-soft); padding-top: 8px; }
.jg-plan-time .jg-signal-title { color: var(--label); }
.jg-plan-time .jg-time-days { font-size: 16px; }
/* What the total is made of. A table rather than sentences: the figures are
   the point, and they line up to be compared. */
.jg-plan-parts {
  width: 100%; border-collapse: collapse; font-size: 13px;
}
.jg-plan-parts th {
  text-align: left; font-weight: 500; color: var(--text-muted); padding: 2px 0;
}
.jg-plan-parts td {
  text-align: right; color: var(--text); font-variant-numeric: tabular-nums;
  white-space: nowrap; padding: 2px 0;
}
/* The groups are separate races, not a running sum, so a gap keeps them from
   reading as one column of figures to add up. */
.jg-plan-parts tbody + tbody th,
.jg-plan-parts tbody + tbody td { padding-top: 10px; }
/* The race the total is waiting on — the tokens or the farming, never both,
   since the total is the longer of the two rather than their sum. */
.jg-parts-lead th, .jg-parts-lead td { color: var(--gold); font-weight: 700; }
/* Time you put in by hand, in the blue a pinned date uses: it is yours, not
   something the plan worked out. */
.jg-parts-manual th, .jg-parts-manual td { color: var(--group-ships); }

/* The add/remove control in the panel head. */
.jg-panel-head { align-items: center; }
.jg-plan-add {
  margin-left: auto; font: inherit; font-size: 13px; font-weight: 700;
  padding: 5px 11px; border-radius: 7px; cursor: pointer;
  background: transparent; border: 1px solid var(--border); color: var(--text-muted);
}
.jg-plan-add:hover { color: var(--text); border-color: var(--gold); }
.jg-plan-add.on { background: var(--gold); border-color: var(--gold); color: #14161f; }

/* Guides already in the plan are marked in the sidebar. */
.jg-nav-item--planned { position: relative; }
.jg-nav-item--planned .jg-nav-name::before {
  content: "\2022"; color: var(--gold); font-weight: 700; margin-right: 5px;
}

/* Below this the plan drops under the cards rather than beside them. */
@media (max-width: 1180px) {
  .jg-layout { flex-wrap: wrap; }
  .jg-plan {
    flex: 1 1 100%;
    padding: 0 24px 24px; max-width: 640px;
  }
}

/* ---- ship cards ----
   swgoh_graphic gives ships edge-to-edge art at the card's full width, while a
   character's portrait sits in a 72.5% box to leave room for the gear ring and
   relic wings around it. That makes ship cards noticeably taller than character
   ones in the same grid. Ships take the same box, so every card in a guide is
   the same size; the art still fills its box, square and unringed. */
.jg-portrait-wrap--ship { width: 72.5%; }

/* The report's footer is capped at the old fixed width too; keep it in step. */
.jg-footer { max-width: var(--page-width); }

/* Cantina energy reads as a resource like the Signal Data types, so it is
   given the same icon-then-number shape rather than a trailing word. */
.jg-chip-cantina b { color: var(--group-ships); }

/* Days and the date they land on, stacked so the number stays prominent. */
.jg-plan-when {
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 1px; margin-left: auto; line-height: 1.25; min-width: 0;
}
/* The running total and what this journey put on it, on one line: the second
   only means anything beside the first. */
.jg-plan-elapsed { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap;
  justify-content: flex-end; }
/* Muted, and not the blue a pinned date uses — this is the plan's own working,
   not anything you set. */
.jg-plan-added {
  font-family: var(--mono); font-size: 11.5px; color: var(--text-muted);
  white-space: nowrap;
}
.jg-plan-date {
  font-family: var(--mono); font-size: 12px; color: var(--text-muted);
  white-space: nowrap;
}
.jg-plan-time .jg-plan-date { font-size: 13px; color: var(--label); }

/* Cards a size up from the report's 170px minimum — the portraits carry the
   gear ring and relic wings, which read better with a little more room. */
.jg-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

.jg-plan-export {
  margin-left: auto; font: inherit; font-size: 12.5px; font-weight: 700;
  padding: 4px 9px; border-radius: 6px; cursor: pointer;
  background: transparent; border: 1px solid var(--border); color: var(--text-muted);
}
.jg-plan-export:hover:not(:disabled) { color: var(--text); border-color: var(--gold); }
.jg-plan-export:disabled { opacity: .55; cursor: default; }

#jg-inventory select {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  font-family: var(--mono); font-size: 14px; padding: 3px 6px; border-radius: 6px;
}
#jg-inventory select:focus { outline: none; border-color: var(--gold); }

/* ---- sidebar height ----
   swgoh_graphic pins the sidebar to the viewport (position:sticky, max-height:
   100vh, overflow-y:auto). Its report has three collapsible groups and fits.
   This site added a fourth, Completed journeys, and the list now runs 710px
   with a roster loaded and about 1170px without one — taller than most windows.
   The overflow then hid whatever fell past the fold behind an inner scrollbar
   that is easy to miss: the last entry in a group would simply not be there.

   The sidebar scrolls with the page instead. A sticky element taller than the
   viewport cannot solve this — its own overflow becomes unreachable — so the
   clamp is removed rather than raised. The farming plan was pinned the same
   way and grew past a screen for the same reason; it is unpinned too, above. */
.jg-nav {
  position: static;
  max-height: none;
  overflow-y: visible;
}

/* Per-character token choice, under the card's name. Auto follows the overall
   threshold; the other three override it for that unit everywhere it appears. */
.jg-token {
  width: calc(100% - 16px); margin: 4px 8px 2px;
  background: var(--surface-2); border: 1px solid var(--border-soft);
  color: var(--text-muted); font-family: var(--mono); font-size: 11.5px;
  padding: 2px 4px; border-radius: 5px; cursor: pointer;
}
.jg-token:hover { color: var(--text); border-color: var(--border); }
.jg-token:focus { outline: none; border-color: var(--gold); }

/* The token a character ends up with, beside its dropdown: bare art, no ring.
   The list says what was chosen, the icon says what that is. */
.jg-token-row { display: flex; align-items: center; gap: 5px; margin: 4px 8px 2px; }
.jg-token-row .jg-token { width: auto; flex: 1 1 auto; min-width: 0; margin: 0; }
.jg-token-mark { flex: 0 0 auto; width: 19px; height: 19px; }

/* The Signal Data icons carry the type, so the word is available to screen
   readers and on hover rather than printed three times across the line. */
.jg-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The two token kinds are counted apart because only Aurodium is bought with
   Era Currency; colouring them apart keeps that visible at a glance. */
.jg-tok-aurodium b { color: var(--gold); }
.jg-tok-kyber b { color: var(--group-ships); }

/* The sum of the Signal Data and shard lines above it. Both spend one cantina
   supply, so the shard line reads as "+ N days" and this is the running total.
   The rule sits under it, closing the sum off from the Lightspeed Token line
   below rather than separating the total from what it adds up. */
.jg-signal-total { border-bottom: 1px solid var(--border-soft); padding-bottom: 9px; }
/* Nothing below it to close off when the guide needs no Lightspeed Tokens. */
.jg-signal-total:last-child { border-bottom: 0; padding-bottom: 0; }

/* ---- pinned plan dates ----
   Every date on the plan is editable. The field is styled down to look like
   the text it replaced, so the panel does not turn into a row of form controls;
   it only asserts itself once a date is actually pinned. */
.jg-plan-date-input {
  font-family: var(--mono); font-size: 12px; color: var(--text-muted);
  background: transparent; border: 1px solid transparent; border-radius: 5px;
  padding: 1px 3px; margin-right: -3px; line-height: 1.25;
  color-scheme: dark; cursor: pointer;
  /* A date input reports a wide intrinsic size; without this it sets the
     column's width instead of fitting into it. */
  max-width: 100%; min-width: 0;
}
.jg-plan-date-input:hover { border-color: var(--border-soft); color: var(--text); }
.jg-plan-date-input:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
/* Blue, not the gold the estimates use: a pinned date is yours, and the two
   should not be mistaken for each other at a glance. */
.jg-plan-date-input.is-pinned {
  color: var(--group-ships); border-color: var(--border-soft);
}
.jg-plan-date-input::-webkit-calendar-picker-indicator { opacity: 0.45; cursor: pointer; }
.jg-plan-date-input:hover::-webkit-calendar-picker-indicator { opacity: 0.9; }

/* What a pin above did to this step: later in gold, earlier in the met green,
   since finishing sooner is the good direction. */
/* How far a date sits from its estimate. Blue like the pinned date it belongs
   to, since a pin is the only thing that puts it there. */
.jg-plan-drift {
  font-family: var(--mono); font-size: 11.5px; color: var(--group-ships);
  white-space: nowrap; opacity: 0.9;
}

/* The plan's own start, above the list. */
.jg-plan-start {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  padding-bottom: 10px; margin-bottom: 4px;
  border-bottom: 1px solid var(--border-soft);
}
.jg-plan-start .jg-plan-date-input { font-size: 14px; color: var(--text); }

/* ---- custom journeys ----
   A group the built-in palette has no colour for, so it gets one here rather
   than in the lifted stylesheet, which belongs to swgoh_graphic. */
.jg-nav-group[data-group="custom"] { --group-custom: #6ee7a8; }

/* The button that makes the first one, sitting where a journey would. */
.jg-nav-new { color: var(--label); font-style: italic; }
.jg-nav-new:hover { color: var(--text); }

/* The editor above a custom journey's cards. Boxed like the collapsible
   panels, so it reads as controls rather than as part of the report. */
.jg-custom {
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 10px; padding: 12px 14px; margin-bottom: 16px;
  display: flex; flex-direction: column; gap: 11px;
}
.jg-custom-head { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.jg-custom-title, .jg-custom-add {
  display: flex; flex-direction: column; gap: 4px; flex: 1 1 220px; min-width: 0;
}
.jg-custom input[type="text"] {
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: 7px;
  padding: 7px 9px; color: var(--text); font-family: var(--mono); font-size: 15px;
  min-width: 0; width: 100%;
}
.jg-custom input[type="text"]:focus-visible {
  outline: 2px solid var(--gold); outline-offset: 1px; border-color: transparent;
}
.jg-custom-delete {
  background: transparent; border: 1px solid var(--border-soft); border-radius: 7px;
  color: var(--text-muted); font-family: var(--mono); font-size: 13px;
  padding: 7px 11px; cursor: pointer; white-space: nowrap;
}
.jg-custom-delete:hover { color: var(--status-pending); border-color: var(--status-pending); }

.jg-custom-list { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: 5px; }
.jg-custom-row {
  display: flex; align-items: center; gap: 9px;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: 8px; padding: 5px 8px;
}
.jg-custom-portrait {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  border: 1px solid var(--border-soft); object-fit: cover;
}
.jg-custom-name {
  font-family: var(--mono); font-size: 14.5px; color: var(--text);
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
/* Where its shards come from, since that is what decides whether a token is
   worth spending on it. */
.jg-custom-node {
  font-family: var(--mono); font-size: 12px; color: var(--group-ships);
  white-space: nowrap;
}
.jg-custom-pick {
  background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 6px;
  color: var(--text); font-family: var(--mono); font-size: 13px;
  padding: 4px 6px; cursor: pointer; flex-shrink: 0;
}
.jg-custom-pick[data-field="stars"] { color: var(--text-muted); }

/* The card's token picker, reused in the editor row so the rule about when a
   token is worth offering lives in one place. It is full width on a card and
   has to sit inline here. */
.jg-custom-row .jg-token-row { margin: 0; flex: 0 0 auto; }
.jg-custom-row .jg-token {
  width: auto; font-size: 13px; padding: 4px 6px; max-width: 132px;
}
/* Nothing to choose — already met, or too deep for any token to help. The gap
   would otherwise make the rows look misaligned. */
.jg-custom-row .jg-custom-tokenless {
  font-family: var(--mono); font-size: 12px; color: var(--text-muted);
  opacity: 0.5; flex-shrink: 0; white-space: nowrap;
}
.jg-custom-drop {
  background: transparent; border: 0; color: var(--text-muted);
  font-size: 15px; line-height: 1; padding: 4px 5px; cursor: pointer; flex-shrink: 0;
}
.jg-custom-drop:hover { color: var(--status-pending); }
.jg-custom-empty {
  margin: 0; font-family: var(--mono); font-size: 13px; line-height: 1.6;
  color: var(--text-muted);
}

/* What a requirement's shards cost, on the card: energy a battle, then the
   cantina mark. The node's name is in the tooltip rather than on the card —
   which node it is changes nothing you can act on, while the cost is what
   decides whether a token is worth spending on that character. */
.jg-card-node {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  margin: 1px 6px 5px; font-family: var(--mono); font-size: 12.5px;
  color: var(--group-ships); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.jg-card-node .jg-signal-icon { width: 15px; height: 15px; }

/* ---- buffers ----
   Time set aside rather than farming done, so the row is quieter than a
   journey: no chips, no estimate, just what you called it and the date the
   plan picks up again. The dashed edge says it is a gap in the work. */
.jg-plan-item--buffer {
  border-style: dashed;
  background: transparent;
}
.jg-plan-item--buffer .jg-plan-step { color: var(--group-ships); opacity: 0.8; }
.jg-plan-item--buffer .jg-plan-body {
  display: flex; align-items: center; gap: 6px 10px; flex-wrap: wrap;
  min-width: 0;
}
.jg-buffer-text {
  flex: 1 1 140px; min-width: 0;
  background: transparent; border: 1px solid transparent; border-radius: 6px;
  padding: 3px 5px; color: var(--text); font-family: var(--mono); font-size: 13px;
}
.jg-buffer-text::placeholder { color: var(--text-muted); opacity: 0.7; }
.jg-buffer-text:hover { border-color: var(--border-soft); }
.jg-buffer-text:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.jg-buffer-days { display: inline-flex; align-items: baseline; gap: 5px; }
.jg-buffer-num {
  width: 52px; text-align: right;
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: 6px;
  padding: 3px 5px; color: var(--group-ships); font-family: var(--mono);
  font-size: 13px; font-variant-numeric: tabular-nums;
}
.jg-buffer-num:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.jg-buffer-label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--label);
}
/* What the buffer holds up: the next journey, or the rest of the plan. A
   toggle rather than a dropdown — there are two answers and the label is the
   answer. */
.jg-buffer-scope {
  background: transparent; border: 1px dashed var(--border-soft); border-radius: 6px;
  color: var(--text-muted); font-family: var(--mono); font-size: 11px;
  padding: 2px 6px; cursor: pointer; white-space: nowrap;
}
.jg-buffer-scope:hover { color: var(--text); border-color: var(--border); }
.jg-buffer-scope[aria-pressed="true"] {
  border-style: solid; border-color: var(--group-ships); color: var(--group-ships);
}
.jg-buffer-scope:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.jg-plan-item--buffer .jg-plan-when {
  flex-direction: row; align-items: center; gap: 6px; flex-wrap: wrap;
}

/* ---- the plan on one line of time ----
   The list above is ordered but not spaced; this is spaced but not ordered.
   Together they answer different questions: what comes next, and how far
   apart. It sits in the plan column and scrolls with it. */
.jg-timeline { margin-top: 14px; }
.jg-tl-rule {
  /* The end dots sit centred on the rule's ends, so the margin has to clear
     their radius — the column clips its overflow now. */
  position: relative; height: 30px; margin: 0 13px;
  border-top: 1px solid var(--border-soft);
}
/* Month ends. Unlabelled on purpose: they are a sense of scale, not data, so
   they sit under the stops and stay out of the way. */
.jg-tl-notch {
  position: absolute; top: 0; width: 1px; height: 7px;
  background: var(--border-soft); transform: translateX(-50%);
}
.jg-tl-mark { position: absolute; top: 0; transform: translate(-50%, -50%); }
.jg-tl-dot {
  display: flex; align-items: center; justify-content: center;
  width: 21px; height: 21px; border-radius: 50%;
  background: var(--surface-2); border: 1px solid var(--gold);
  color: var(--gold); font-family: var(--mono); font-size: 11.5px;
  font-variant-numeric: tabular-nums; cursor: default;
}
.jg-tl-mark:hover .jg-tl-dot { background: var(--gold); color: var(--surface); }
/* A buffer is a gap in the work, so it is a smaller unnumbered stop in the
   same blue its date input uses. */
.jg-tl-mark.is-buffer .jg-tl-dot {
  width: 12px; height: 12px; border-color: var(--group-ships);
  border-style: dashed; background: var(--surface);
}
.jg-tl-mark.is-buffer:hover .jg-tl-dot { background: var(--group-ships); }
.jg-tl-ends {
  display: flex; justify-content: space-between; margin-top: 3px;
  font-family: var(--mono); font-size: 10.5px; color: var(--text-muted);
}

/* ---- phones ----
   Everything below this line is mobile only. The desktop layout is a sidebar
   of journeys beside a grid of cards, and neither half survives being made
   narrow: the sidebar becomes a wall of buttons above the thing you came to
   read, and 200px cards leave room for one and a half per row.

   The breakpoint matches the one in journey-guide.css, which is where the
   layout already stacks. That file is generated from swgoh_graphic, so these
   are overrides here rather than edits there. */
@media (max-width: 760px) {

  /* --- the journey list, as a drawer --- */

  /* Sticky, so the way back to the list is always one tap away however far
     down the requirements you have scrolled. */
  .jg-nav {
    position: sticky; top: 0; z-index: 20;
    padding: 8px 12px; gap: 8px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-soft);
  }
  .jg-nav-toggle {
    display: flex; align-items: center; gap: 9px;
    flex: 1 0 100%; width: 100%;
    background: var(--surface); border: 1px solid var(--border-soft);
    border-radius: 8px; padding: 9px 12px;
    color: var(--text); font: inherit; font-size: 14px; cursor: pointer;
    text-align: left;
  }
  .jg-nav-toggle-label {
    font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--text-muted); flex: 0 0 auto;
  }
  /* The journey you are on. It takes the leftover width and truncates, so a
     long name cannot push the chevron off the row. */
  .jg-nav-toggle-current {
    flex: 1 1 auto; min-width: 0; font-weight: 700; color: var(--gold);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .jg-nav-toggle-chevron { flex: 0 0 auto; transition: transform .15s ease; }
  .jg-nav--shut .jg-nav-toggle-chevron { transform: rotate(-90deg); }
  .jg-nav--shut .jg-nav-group { display: none; }
  /* Open, the list is the page: it scrolls on its own rather than pushing the
     requirements arbitrarily far down. */
  .jg-nav:not(.jg-nav--shut) { max-height: 82vh; overflow-y: auto; }

  /* --- smaller cards --- */

  /* Four to a row on a phone instead of one and a half. The portrait is a
     percentage of the card, so shrinking the track shrinks the art, the gear
     ring and the relic wings together and keeps them in register. */
  .jg-grid { grid-template-columns: repeat(auto-fill, minmax(min(88px, 100%), 1fr)); gap: 8px; }
  .jg-card { padding: 8px 0 4px; border-width: 1.5px; border-radius: 8px; }
  .jg-name { font-size: 10.5px; margin-top: 2px; padding: 0 3px 2px; }

  /* The ribbon is a fixed 88px square on the desktop card, which is the whole
     card here — it has to come down with everything else or it swallows it. */
  .jg-ribbon { width: 42px; height: 42px; }
  .jg-ribbon-req { top: 2px; right: 3px; font-size: 8.5px; }

  .jg-badge {
    min-width: 19px; height: 16px; padding: 0 4px;
    border-radius: 8px; border-width: 1.5px; font-size: 9.5px;
  }
  .jg-stars { font-size: 9px; }
  .jg-card-node { font-size: 9.5px; gap: 2px; margin: 0 2px 3px; }
  .jg-card-node .jg-signal-icon { width: 10px; height: 10px; }

  /* The token control keeps a tappable height even though everything around
     it shrank: it is the one thing on the card you are meant to hit. */
  .jg-token-row { gap: 4px; margin: 3px 4px 1px; }
  .jg-token-row .jg-token { font-size: 11px; padding: 3px 4px; min-height: 30px; }
  .jg-token-mark { width: 15px; height: 15px; }

  /* --- the rest of the page --- */

  .jg-main { padding: 12px 12px 20px; }
  .jg-plan { padding: 0 12px 20px; }
}
