/* The shared component vocabulary.
 *
 * The layering is tokens -> components -> pages:
 *
 *   tokens-parchment.css   what the colours, sizes and timings ARE
 *   components.css         what a button, panel, chip and meter ARE  <- here
 *   site.css               the chrome around a page: nav, shell, footer, forms
 *   <page>.css             what is genuinely true of one screen only
 *
 * head.php loads the first three on every page, in that order, so nothing has to
 * opt in. Rules here may use tokens and nothing else — no raw hex, no magic
 * pixel values that a token already names.
 *
 * WHY THIS FILE EXISTS. Before it, every screen re-derived its own panels and
 * chips, so "make the app look like a game menu" meant editing six stylesheets
 * and hoping they agreed. They already did not: the card was written out twice
 * identically in journal.css and quest-builder.css, the chip was spelled three
 * different ways, and the two button systems had drifted apart under a comment
 * telling us to keep them in sync by hand. That comment was the same class of
 * problem as the assets/js/quests.js <-> lib/quests.php mirrors — correct when
 * written, silently wrong later — and it had already gone wrong.
 *
 * WHAT IS AND IS NOT SHARED. Each component below carries only the half its
 * call sites genuinely agree on — the pill radius, the selected state, the
 * frame, the fill. Sizes, paddings and grounds still differ per page and stay
 * in that page's stylesheet, flagged with a comment. Collapsing them would move
 * pixels, and this layer is deliberately introduced without a restyle so the
 * restyle can be judged on its own. Those differences were never decided; they
 * accumulated. The mockups should settle them, and when they do the page-level
 * leftovers disappear into the base here.
 *
 * NAMING. These names are the local half of a contract: the Claude Design
 * system uses the same ones, so a mockup translates into class names rather
 * than guesswork. Do not rename one side alone.
 *
 * Legacy class names appear alongside the new ones as selector aliases, so no
 * markup had to move to introduce this layer. They retire page by page as each
 * screen is restyled.
 */

/* ==========================================================================
   BUTTONS
   One definition, three variants. Previously .ag-btn-* lived in the token file
   and .btn-* lived in site.css, with a hand-maintained "keep in sync" comment
   between them — and they had already drifted: .ag-btn-primary labelled itself
   in --text-on-dark (a token meant for the night band, 5.34:1 on seal) while
   .btn-primary hardcoded #FFF8F0 (6.44:1). The brighter one won, and it has a
   token: --text-on-accent, which is the name the design system's tokens/colors.css
   has used all along. This file briefly called it --text-on-action, which broke
   the one contract the layer exists to keep — that a name means the same thing
   on both sides. Do not rename it on one side alone.
   ========================================================================== */

.ag-parchment .ag-btn-primary,
.ag-parchment .btn-primary,
.ag-parchment .ag-btn-secondary,
.ag-parchment .btn-secondary{
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-ui);font-size:var(--fs-md);font-weight:600;
  border-radius:var(--radius-sm);cursor:pointer;text-decoration:none;
}

.ag-parchment .ag-btn-primary,
.ag-parchment .btn-primary{
  background:var(--accent-primary);color:var(--text-on-accent);
  border:none;padding:13px 28px;
  transition:transform var(--dur-fast) var(--ease-standard),
             background var(--dur-fast) var(--ease-standard),
             box-shadow var(--dur-fast) var(--ease-standard);
}
.ag-parchment .ag-btn-primary:hover,
.ag-parchment .btn-primary:hover{
  background:var(--accent-primary-hover);
  transform:var(--lift-hover,translateY(-2px));box-shadow:var(--shadow-action);
}
.ag-parchment .ag-btn-primary:active,
.ag-parchment .btn-primary:active{ background:var(--accent-primary-press);transform:scale(0.985) }
/* .btn-primary never had a disabled state; it does now, by joining this rule. */
.ag-parchment .ag-btn-primary:disabled,
.ag-parchment .btn-primary:disabled{ opacity:0.45;cursor:default;transform:none;box-shadow:none }

.ag-parchment .ag-btn-secondary,
.ag-parchment .btn-secondary{
  background:none;color:var(--text-strong);
  border:1px solid var(--border-default);padding:12px 27px;
  transition:background var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard);
}
.ag-parchment .ag-btn-secondary:hover,
.ag-parchment .btn-secondary:hover{ background:var(--surface-gold-quiet);border-color:var(--gold-600) }
.ag-parchment .ag-btn-secondary:disabled,
.ag-parchment .btn-secondary:disabled{ opacity:0.45;cursor:default }

/* The quiet variant reads as a link rather than a button: for a real decision
   that should not compete with the primary one beside it ("Leave personal" next
   to Promote and Merge). It was defined in admin.css, which meant a button
   variant lived inside one page's stylesheet. */
.ag-parchment .ag-btn-quiet{
  font-family:var(--font-ui);font-size:var(--fs-sm);
  background:none;border:none;padding:5px 4px;
  color:var(--text-muted);cursor:pointer;
  text-decoration:underline;text-underline-offset:3px;
}
.ag-parchment .ag-btn-quiet:hover{ color:var(--text-body) }
.ag-parchment .ag-btn-quiet:focus-visible{ outline:none;box-shadow:var(--focus-ring);border-radius:var(--radius-sm) }
.ag-parchment .ag-btn-quiet:disabled{ opacity:0.45;cursor:default;text-decoration:none }

/* ==========================================================================
   PANEL / CARD
   A panel is the framed surface. A card is a panel that has agreed to a
   standard padding and a hairline shadow. Six places were re-deriving these
   three declarations; .jr-day and .qb-card were byte-for-byte identical, and
   both spelled their radius as a raw 10px even though --radius-lg is 10px.
   ========================================================================== */

.ag-panel,
.jr-day, .jr-quest, .qb-card, .auth-card, .admin-row, .feature-card{
  background:var(--surface-card);
  border:1px solid var(--border-hairline);
  border-radius:var(--radius-lg);
}

/* Surface variants, mapped to the tokens that already exist. */
.ag-panel--raised{ background:var(--surface-raised) }
/* Applied alongside .ag-panel, so the frame is already there. */
.ag-panel--sunken,
.qb-card-sunken{ background:var(--surface-sunken);box-shadow:none }
/* The full shorthand, not just a colour: --inset is applied to elements that do
   not also carry .ag-panel (the builder's task blocks), so there is no width for
   a bare border-color to tint. */
.ag-panel--inset,
.qb-task{ background:var(--surface-inset);border:1px solid var(--border-rule) }
/* Not a surface so much as an absence of one — used where a row needs the
   panel's shape and spacing but should not read as a raised object. */
.ag-panel--bare,
.admin-row-quiet{ background:transparent }

.ag-card{
  padding:clamp(18px, 3vw, 28px);
  box-shadow:var(--shadow-sm);
}

/* Radii that differ from the base, kept until the restyle settles them.
   --radius-md is the smaller card shape; --radius-sm the tightest. */
.ag-panel--tight,
.feature-card{ border-radius:var(--radius-md) }
.admin-row{ border-radius:var(--radius-sm) }

/* ==========================================================================
   ROW
   The list item, which had three spellings: .card-list li on the guilds page,
   .admin-row in the review queue, and the journal's occurrence rows. What they
   share is a horizontal band with a baseline-aligned gap that wraps rather than
   overflows; what they do not share is their separator, which is why the
   hairline is a modifier rather than part of the base.
   ========================================================================== */

.ag-row{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:var(--space-5);
  min-width:0;
}
.ag-row--baseline{ align-items:baseline }
/* Separated by a rule rather than a gap. The first child loses its border so a
   list does not open with a stray line. */
.ag-row--ruled{ border-top:1px solid var(--border-hairline) }
.ag-row--ruled:first-child{ border-top:0 }

/* ==========================================================================
   CHIP
   A small pill-shaped control that is either on or off. Four call sites had
   invented it separately: the wizard's ability chips, the builder's taker
   chips and day toggles, and the journal's view tabs.

   WHAT IS SHARED IS WHAT IS HERE. The pill radius, the pointer, the resting
   text colour, the hover tell and — the part that was genuinely inconsistent —
   the selected state, which existed as two spellings of near-white (#FFF8F0
   and --parchment-50) on the same seal ground. Both now resolve to
   --text-on-accent.

   WHAT IS NOT SHARED STAYS ON THE PAGE. Size, padding, background and border
   colour still differ per call site, and deliberately are not unified here:
   collapsing them would move pixels, and this layer is being introduced without
   a restyle so that the restyle can be judged on its own. The differences were
   never designed — they accumulated — and the mockups should settle them. Note
   also that .jr-tab is an <a> and the rest are <button>s, so the base must not
   impose a `display`.
   ========================================================================== */

.ag-chip,
.qb-chip, .qb-day, .jr-tab{
  border-radius:var(--radius-pill);
  color:var(--text-body);
  cursor:pointer;
  text-decoration:none;
  transition:background var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard),
             color var(--dur-fast) var(--ease-standard);
}
.ag-chip:hover,
.qb-chip:hover, .qb-day:hover, .jr-tab:hover{ border-color:var(--gold-600) }

/* On. `.is-selected` is the wizard's spelling and `.is-on` the other three's;
   both are kept because retiring one means editing creation-ui.js. */
.ag-chip.is-selected, .ag-chip.is-on,
.qb-chip.is-on, .qb-day.is-on, .jr-tab.is-on{
  background:var(--accent-primary);
  border-color:var(--accent-primary);
  color:var(--text-on-accent);
}

/* Off, but visible. A chip the current settings rule out is greyed rather than
   hidden, so the rule teaches itself: you can see the option, and the tooltip
   tells you what to widen first. */
.ag-chip.is-off,
.qb-chip.is-off{
  opacity:.42;cursor:not-allowed;
  background:var(--surface-inset);color:var(--text-muted);
  border-color:var(--border-hairline);
}

/* The base's own metrics, for new markup and the gallery. The legacy aliases
   above keep theirs in their page stylesheets until the restyle. */
.ag-chip{
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-ui);font-size:var(--fs-xs);
  padding:5px 11px;
  background:transparent;
  border:1px solid var(--border-default);
}

/* ==========================================================================
   BADGE
   A chip's non-interactive twin: a status word, never a control. Promoted out
   of journal.css, where .jr-badge, .jr-occ-skill, .jr-shared and .jr-prog-est
   were four spellings of the same pill. As with the chip, the shared half is
   here and the sizes stay where they are.
   ========================================================================== */

.ag-badge,
.jr-badge, .jr-occ-skill, .jr-shared, .jr-prog-est{
  border-radius:var(--radius-pill);
  background:var(--surface-inset);
  color:var(--text-muted);
  white-space:nowrap;
}
/* The gold variant marks something the Guild itself vouches for — a party
   quest, a canonical skill — and is the one place a badge takes the accent. */
.ag-badge--accent,
.jr-badge-party, .jr-occ-skill, .jr-shared{
  background:var(--surface-gold-quiet);
  color:var(--text-accent);
}
.ag-badge{
  display:inline-flex;align-items:center;
  font-size:var(--fs-3xs);font-weight:600;
  text-transform:uppercase;letter-spacing:.08em;
  padding:3px 9px;
}

/* ==========================================================================
   METER
   A horizontal progress bar. Three existed: .xp-bar on the account page, the
   wizard's .ag-cc-score-bar and the journal's .jr-bar. The first two carried
   the same gold gradient written out as raw hex in two files, bypassing the
   token layer entirely; that gradient is now --fill-meter, which is the real
   win here. Heights and grounds still differ per call site.
   ========================================================================== */

.ag-meter,
.xp-bar, .ag-cc-score-bar, .jr-bar{
  border-radius:var(--radius-pill);
  overflow:hidden;
}
.ag-meter-fill,
.xp-bar-fill, .ag-cc-score-fill{
  height:100%;
  border-radius:var(--radius-pill);
  background:var(--fill-meter);
}
/* The base's own metrics, for new markup and the gallery. Each existing meter
   keeps its height and ground in its page stylesheet — see the note above. */
.ag-meter{ display:block;height:6px;background:var(--surface-sunken) }
.ag-meter-fill,
.xp-bar-fill{ display:block;transition:width var(--dur-slow) var(--ease-standard) }

/* ==========================================================================
   TABS
   A row of chips acting as one choice. The strip is the component; the tabs
   themselves are chips, which is why there is so little here.
   ========================================================================== */

.ag-tabs{ display:flex;flex-wrap:wrap;gap:var(--space-4);margin-bottom:var(--space-7) }

/* ==========================================================================
   THE MARK PICKER
   A task's icon and colour — yours, not the quest's. Drawn by
   lib/partials/mark-picker.php on the journal's agenda rows and on the quest
   sheet's habit rows, which is why the rules are here rather than in either
   page's stylesheet. Moved verbatim out of journal.css; the jr- prefix stays so
   the journal's own overrides keep matching.

   The whole control is a <details> holding radios, so none of this depends on a
   script: [hidden] on an icon is the search filter's only reach into it, and the
   dialog below is [open] and CSS. See docs/mark-picker-dialog-plan.md.
   ========================================================================== */

.jr-mark{display:inline-flex;align-items:center;justify-content:center}
.jr-mark-pick{flex:0 0 auto}
.jr-mark-pick summary{
  display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;margin-top:2px;cursor:pointer;list-style:none;
  border:1px solid currentColor;border-radius:var(--radius-xs);
}
.jr-mark-pick summary::-webkit-details-marker{display:none}
.jr-mark-pick summary:focus-visible{outline:none;box-shadow:var(--focus-ring)}

/* The scrim. It is a pseudo-element on the <details> rather than a real one in
   the partial because [open] is the only state there is, and a div that exists
   only when a CSS selector says so is a div the no-script path would have to
   carry anyway. */
.jr-mark-pick[open]::before{
  content:'';position:fixed;inset:0;z-index:80;
  background:rgba(28,26,20,0.4);
}

/* Fixed and centred, which is the fix for three of the Big Bug List's five
   complaints at once.

   It used to open in flow inside the agenda row, which pushed the rest of the
   day down the page and left the panel with no stacking context of its own — so
   the row above's Move disclosure (z-index 5), the day's add-a-job form (6) and
   the sticky sub-tab bar (2) all painted over the top of it. That is what "you
   can see things popping in from underneath" was. Out of flow and above the lot
   of them, neither can happen.

   The height is capped and the grid inside takes the slack, so a hundred-odd
   marks scroll within the card instead of growing it past the screen. */
.jr-picker{
  position:fixed;z-index:81;
  top:50%;left:50%;transform:translate(-50%,-50%);
  width:min(420px, calc(100vw - 2rem));max-height:min(80vh, 640px);
  display:flex;flex-direction:column;
  box-sizing:border-box;padding:var(--space-6);
  background:var(--surface-card);border:1px solid var(--gold-700);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);
}
.jr-picker > form{display:flex;flex:1 1 auto;min-height:0;flex-direction:column;gap:var(--space-5)}
.jr-picker-head{display:flex;gap:var(--space-5);align-items:center;justify-content:space-between}
.jr-picker-heading{display:flex;flex-direction:column;gap:2px;min-width:0}
/* text-wrap:pretty as the designs have it — a task title is one line of prose
   and a two-word orphan on the second line reads as a mistake. */
.jr-picker-task{font-size:var(--fs-sm);font-weight:600;color:var(--text-strong);text-wrap:pretty}
.jr-picker-label{
  margin:0;font-family:var(--font-display);font-size:var(--fs-3xs);
  font-weight:600;letter-spacing:var(--track-label);text-transform:uppercase;color:var(--text-accent);
}
.jr-picker-note{flex:0 0 auto;font-size:var(--fs-xs);color:var(--text-muted)}
.jr-tint-row{display:flex;gap:var(--space-4);align-items:flex-start}
.jr-tints{display:flex;flex:1 1 auto;gap:var(--space-4);flex-wrap:wrap}
.jr-tint{position:relative;display:block;width:32px;height:32px;cursor:pointer}
.jr-tint input{position:absolute;inset:0;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.jr-tint-face{
  display:block;width:100%;height:100%;border-radius:var(--radius-pill);
  background:var(--mark);border:1px solid var(--border-hairline);box-sizing:border-box;
}
.jr-tint input:checked + .jr-tint-face{border:2px solid var(--text-strong)}
.jr-tint input:focus-visible + .jr-tint-face{box-shadow:var(--focus-ring)}

/* The fifteenth swatch, which is now one control and not two.

   The wheel is the designs' own conic gradient: it stands for "some colour", so
   it is deliberately not any one of the fourteen beside it. The colour well used
   to be a sixteenth swatch sitting after it, which meant tapping the wheel ticked
   a radio and showed nothing — "selecting the colour wheel picker is doing
   nothing", as the Big Bug List put it. The well now lies invisibly across the
   wheel, so tapping it opens the platform's chooser; the radio is underneath and
   still in the radio group, so a keyboard still reaches it. Last in the row,
   which is where the designs put it. */
.jr-tint-face-any{
  background:conic-gradient(#A6321F,#C2571F,#D9A21B,#6E7B3A,#2F6F6A,#2E4A6B,#6B3F5E,#A8566B,#A6321F);
}
/* Later in the DOM than the radio, so it takes the taps; the radio keeps the
   keyboard. Browsers draw a colour input with a border and padding of their own,
   and none of it shows through an opacity of zero. */
.jr-tint-well{
  position:absolute;inset:0;width:100%;height:100%;
  padding:0;margin:0;border:none;background:none;opacity:0;cursor:pointer;
}
.jr-tint-well:focus-visible{outline:none}
.jr-tint-any:focus-within .jr-tint-face-any{box-shadow:var(--focus-ring)}

.jr-picker-search{
  flex:0 0 auto;width:100%;box-sizing:border-box;min-height:44px;
  padding:0 var(--space-5);font-family:var(--font-ui);font-size:var(--fs-sm);
  color:var(--text-strong);background:var(--surface-inset);
  border:1px solid var(--border-rule);border-radius:var(--radius-xs);
}
/* The grid is what gives when the card runs out of room, so the header, the
   colours and the search box stay put while the marks scroll under them. */
.jr-picker-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;padding:2px}
/* Eight across, as the designs' dialog has, and taller than it was: the set is a
   hundred-odd marks now and a grid sized for forty-seven made the scroll do all
   the work. */
.jr-icons{
  display:grid;grid-template-columns:repeat(8,1fr);gap:var(--space-4);
}
.jr-icon{position:relative;display:block;height:44px;cursor:pointer}
.jr-icon input{position:absolute;inset:0;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
/* Opaque, not --surface-inset. A tile at 55% let the scrim and whatever was
   behind it show through the grid, which is the other half of "transparent where
   the marks are". */
.jr-icon-face{
  display:flex;align-items:center;justify-content:center;height:100%;
  border-radius:var(--radius-xs);box-sizing:border-box;
  border:1px solid var(--border-hairline);background:var(--surface-card);
}
.jr-icon input:checked + .jr-icon-face{border-color:var(--accent-primary);background:var(--surface-gold-quiet)}
.jr-icon input:focus-visible + .jr-icon-face{box-shadow:var(--focus-ring)}
.jr-icon[hidden]{display:none}
.jr-picker-none{margin:var(--space-5) 2px 0;font-size:var(--fs-xs);color:var(--text-muted)}

/* The picker's Done, and the journal's save buttons, which wear the same pill.
   Its per-page sizing overrides stay in journal.css. */
.jr-save-btn {
  font-family: var(--font-ui); font-size: .875rem; font-weight: 600;
  padding: 7px 16px; cursor: pointer;
  border-radius: var(--radius-pill);
  border: 1px solid var(--accent-primary);
  background: var(--accent-primary); color: var(--text-on-accent);
}
.jr-picker-head .jr-save-btn{flex:0 0 auto;min-height:40px}
.jr-save-btn:hover { background: var(--accent-primary-hover); }

@media (max-width: 860px){
  .jr-icons{grid-template-columns:repeat(6,1fr)}
}
@media (max-width: 560px){
  .jr-icons{grid-template-columns:repeat(5,1fr)}
  /* The panel used to be squeezed to calc(100vw - 7rem) because it opened from a
     control already indented into the row and inherited that offset. Centred on
     the viewport it has no offset to pay for, so it keeps the page's own margin
     and nothing more. */
  .jr-tint-row{flex-direction:column;gap:var(--space-3)}
}

/* ==========================================================================
   UTILITIES
   ========================================================================== */

/* Present to screen readers, absent to everything else. Was in admin.css. */
.visually-hidden{
  position:absolute;
  width:1px;height:1px;
  margin:-1px;padding:0;
  overflow:hidden;
  clip:rect(0 0 0 0);
  white-space:nowrap;
  border:0;
}
