/* auth.css — the shared token layer and styling for every authed page:
 * login/register/verify/forgot/reset, admin, and the customer DAM (library,
 * viewer, portal). Ten pages read these variables, so this file is the theme.
 *
 * LIGHT, and measured rather than eyeballed. Every pair below was checked
 * against WCAG 2.2: body text needs 4.5:1, and the boundary of a control that
 * has no other visual affordance needs 3:1 (1.4.11 non-text contrast).
 *
 * Re-measured 2026-08-15 when the palette moved onto the Pack OS brand (below):
 *
 *   fg on panel  17.76:1     muted on panel  5.97:1     accent on panel 5.22:1
 *   fg on bg     17.01:1     muted on bg     5.72:1     accent on bg    5.00:1
 *   good 4.62  ·  warn 4.81  ·  err 4.62      (on bg, the harder surface)
 *   line-strong on panel 3.37:1, on bg 3.22:1
 *
 * TWO BORDER TOKENS, because they do different jobs and only one of them is
 * governed by 1.4.11. `--line` divides cards and table rows: decorative, and
 * pushing it to 3:1 would draw a hard grid over everything. `--line-strong` is
 * the boundary of an input or a control that is otherwise just a rectangle —
 * that one has to clear 3:1 or the control has no perceivable edge. The dark
 * theme used ONE border token at 1.31:1 for both, so every text field in the
 * product was delineated below the threshold.
 *
 * THREE BRAND COLORS COULD NOT SURVIVE THE FLIP UNCHANGED, and the rule is the
 * same for all three: a palette drawn for a marketing page is drawn for large
 * type and big fills, and this file sets 13px body text and 1px control edges.
 * The teal (#05c6b8, 2.06:1 here), the green (#1f9d61, 3.32:1) and the coral
 * (#f0684c, 2.96:1) are each carried down in lightness until they clear their
 * floor — the same hue at a legible lightness, not a different color. Each
 * keeps its as-drawn value under a `-bright` name for fills, dots and glows,
 * which are the places the brand color belongs and text never is.
 */
:root {
  color-scheme: light;
  /* ---- THE PACK OS PALETTE ---------------------------------------------
   * These are site/site.css's values, not near-misses of them. The signed-in
   * product used to run on a generic blue (#2563eb, 40 call sites) with the
   * brand teal defined one line below it and used 7 times — so a customer
   * crossed from a teal marketing site into a blue app at the moment they
   * signed in, which is the moment the brand should be most settled.
   *
   * WHICH TEAL, AND WHY IT IS NOT THE BRIGHT ONE. --teal #05c6b8 is the mark's
   * color and it cannot carry text: measured, white on it is 2.15:1 and it on
   * paper is 2.06:1, against the 4.5 AA floor. --teal-text #067a6f passes both
   * (5.22 and 5.00) and lands almost exactly where the old blue sat (5.17 and
   * 4.95), so this is a like-for-like swap rather than a contrast regression.
   * The bright teal is kept as --accent-bright for things that are NEVER
   * behind text: progress fills, indicator dots, the focus glow. */
  --bg: #fbfaf7;                    /* --paper: warm stock, not cool grey */
  --panel: #ffffff;                 /* --card */
  --line: rgba(22, 24, 29, .10);    /* --line */
  /* .50, NOT the site's .18. --line-strong is the boundary of a control that is
   * otherwise just a rectangle, so WCAG 1.4.11 puts a 3:1 floor under it. The
   * site's own value is a decorative divider and measures 1.84:1 here — taking
   * it verbatim would have left every text field in the product delineated
   * below the threshold, which is the exact defect the dark theme had. */
  --line-strong: rgba(22, 24, 29, .50);
  --fg: #16181d;                    /* --ink: near-black, warm — was blue-slate */
  --muted: #5b6474;                 /* --mut */
  --dim: #98a0af;                   /* --dim */
  --accent: #067a6f;                /* --teal-text: the ACCESSIBLE teal */
  --accent-bright: #05c6b8;         /* --teal: the mark. Never behind text. */
  --accent-deep: #03a396;           /* --teal-deep: hover/pressed */
  /* THE BRAND'S STATUS COLORS ARE DISPLAY COLORS, and taking them literally
   * was a contrast regression I nearly shipped. --grn #1f9d61 measures 3.32:1
   * on this ground and --coral #f0684c 2.96:1, both under the 4.5 AA floor —
   * where the values they replaced sat at 4.81 and 6.02. These are the same
   * hues carried down in lightness until they clear 4.5 on --bg, which is the
   * harder of the two surfaces (--panel is pure white and flatters everything;
   * solve against the darker one or the page passes and the cards do not). */
  --good: #1a8251;                  /* --grn, darkened: 4.82 panel / 4.62 bg */
  --warn: #b45309;                  /* unchanged: already 5.02 / 4.81 */
  --err: #bb513b;                   /* --coral, darkened: 4.82 / 4.62 */
  --err-bright: #f0684c;            /* --coral as drawn: fills and marks, never text */
  --pk-teal: #067a6f;               /* kept: existing call sites, now on-brand */
  /* the process set. A packaging company's brand palette IS process color, and
   * the viewer's separations rail is the one place in the product that draws
   * ink for a living — it should draw it in these. */
  --ink-cyan: #00b7e8; --ink-mag: #f0368f; --ink-yel: #f2c53d; --ink-key: #16181d;
  /* the artwork surround. Packaging is judged against white stock, so the page
   * sits on a neutral that lets a white substrate keep an edge instead of
   * dissolving into the chrome. */
  --canvas: #e8eaed;
  /* elevation: on light, shadow does the work the panel/bg contrast did on dark */
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, .06), 0 1px 1px rgba(15, 23, 42, .04);
  --shadow-md: 0 4px 12px rgba(15, 23, 42, .08), 0 1px 3px rgba(15, 23, 42, .06);
  --shadow-lg: 0 12px 32px rgba(15, 23, 42, .12), 0 2px 8px rgba(15, 23, 42, .06);
  --focus: 0 0 0 3px rgba(5, 198, 184, .38);   /* --teal at 38%: a glow, never behind text */

  /* ---- THE SCALE -------------------------------------------------------
   * Measured before this existed: 22 distinct font sizes across these pages,
   * 10 border radii, 6 font weights (four of them different bolds: 640, 680,
   * 700, 750), and 13 gap values off any grid. The half-pixel type sizes —
   * 10.5, 11.5, 12.5, 13.5, 14.5 — are the signature of the problem: nobody
   * chose them, they were nudged one at a time in the file that needed them.
   *
   * Six type steps, one spacing rhythm, four radii, three weights. A value
   * that is not on this list is a decision someone should have to defend. */
  --t-xs: 11px;      /* chips, badges, eyebrow labels */
  --t-sm: 12px;      /* secondary text, notes, small controls */
  --t-md: 13px;      /* body — the default */
  --t-lg: 14px;      /* emphasis, card and row titles */
  --t-xl: 16px;      /* section headings */
  --t-2xl: 20px;     /* page title */

  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px;

  --r-sm: 6px;       /* chips and small controls */
  --r-md: 8px;       /* buttons, inputs */
  --r-lg: 12px;      /* cards and panels */
  --r-full: 999px;

  --w-normal: 400; --w-medium: 600; --w-bold: 700;

  /* The reading measure. Prose ran at 156 characters per line against a 45–75
   * optimum, because nothing capped it — past ~90 the eye loses the return.
   *
   * 56ch, not 68ch, because the CSS `ch` unit is the width of the "0" glyph and
   * "0" is wider than the average lowercase letter — so `68ch` renders about 83
   * actual characters. Measured in the browser: 56ch lands at ~68 characters,
   * which is the number that matters. */
  --measure: 56ch;
}
* { box-sizing: border-box; }
body {
  margin: 0; min-height: 100vh; background: var(--bg); color: var(--fg);
  /* site/site.css --font, verbatim. Inter is NAMED, not loaded — the marketing
   * site does not ship a webfont either, so on a machine that has Inter both
   * halves of the product now use it, and on one that does not both fall back
   * the same way. The old stack did not name it at all, so the two halves
   * disagreed on exactly the machines where it was installed. */
  font: 14px/1.55 "Inter", "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.auth-card {
  width: 100%; max-width: 380px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s-6) var(--s-5);
  box-shadow: var(--shadow-lg);
}
/* THE REAL MARK, not a CSS impression of it. The header used to render
 * `pack<span class=os>OS</span>` — text imitating a wordmark — while eight
 * proper marks sat in site/assets/brand/ in color, black, white and
 * reversed. The imitation could never match: the wordmark is drawn letterforms,
 * not a system font with a tinted suffix. .os is kept below because the auth
 * pages still use the text form inside their card. */
.brand { display: inline-flex; align-items: center; gap: var(--s-2); font-weight: var(--w-bold); font-size: var(--t-xl); letter-spacing: -0.2px; margin-bottom: var(--s-1); text-decoration: none; color: inherit; }
.brand img { display: block; height: 21px; width: auto; }
.brand .os { color: var(--pk-teal); }
h1 { font-size: var(--t-2xl); margin: var(--s-3) 0 var(--s-1); font-weight: var(--w-bold); letter-spacing: -0.2px; }
.sub { color: var(--muted); font-size: var(--t-md); margin: 0 0 var(--s-5); max-width: var(--measure); }
label { display: block; font-size: var(--t-sm); color: var(--muted); margin: var(--s-3) 0 var(--s-1); }
/* NOT EVERY INPUT IS A TEXT FIELD. This rule was unqualified, so `width:100%`
 * landed on checkboxes and radios too — and a checkbox cannot refuse a width.
 * Measured on the viewer: all 16 checkboxes rendered between 35px and 137px
 * wide, each one stretching across its row and shoving its own label to the
 * right. That is what made the layer rail's nine names right-aligned with
 * left edges ragged across 40px, and wrapped two of them onto extra lines.
 * It affected every checkbox on all ten pages that load this file. */
input:not([type='checkbox']):not([type='radio']):not([type='range']),
select, textarea {
  width: 100%; color: var(--fg); background: var(--panel);
  /* NOT the `font` shorthand. Three :not() clauses give this rule a
   * specificity of (0,3,1), which beats a surface's own `.searchbar
   * input[type=search]` at (0,2,1) — so `font: inherit` was resetting those
   * fields to the body's 14px and silently overriding the type scale. Naming
   * the properties keeps the default on-scale and lets a surface override it. */
  font-family: inherit; font-size: var(--t-md); line-height: 1.4;
  /* the boundary IS the affordance here, so it takes the 3:1 token */
  border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: 9px 11px; min-height: 36px;
}
input[type='checkbox'], input[type='radio'] {
  width: 16px; height: 16px; flex: none; margin: 0; accent-color: var(--accent);
  /* they render no text, but they inherit the UA's 13.333px and that was the
   * only size left outside the scale — an audit that reports one stray value
   * gets ignored, so it is worth the line */
  font-size: var(--t-md);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: var(--focus);
}
input[type='checkbox']:focus-visible, input[type='radio']:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none;
}
/* ---- THE BUTTON ---------------------------------------------------------
 * There was no button component, only button-shaped CSS repeated per file.
 * `.btn` was defined FOUR times in four files with four different paddings —
 * 9/16 in the library, 7/13 in the viewer, 10/18 in the portal, 10/14 here —
 * and across the product ~10 distinct paddings served three actual roles.
 *
 * One component, three variants, two sizes. Variants are about CONSEQUENCE,
 * not decoration: primary is the one thing this screen wants you to do,
 * secondary is a real alternative, ghost is a tertiary action that should not
 * compete. `danger` is a modifier because destructive is orthogonal to rank.
 *
 * Every variant shares the same box, so a row of mixed buttons lines up on one
 * baseline and one height — which is what makes a toolbar look built rather
 * than assembled. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  font: inherit; font-size: var(--t-md); font-weight: var(--w-medium); line-height: 1.2;
  padding: 9px var(--s-4); min-height: 36px; border-radius: var(--r-md);
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
  white-space: nowrap; transition: background-color .12s ease, border-color .12s ease;
}
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: none; box-shadow: var(--focus); }
.btn:disabled, .btn[aria-disabled='true'] { opacity: .5; cursor: not-allowed; }

.btn, .btn--primary, button.primary {
  color: #fff; background: var(--accent); border-color: var(--accent);
}
.btn:not(:disabled):hover, .btn--primary:not(:disabled):hover,
/* the retired blue's dark step, missed when the palette moved — these three
 * literals outlived the token swap because nothing reads a hover through a
 * variable unless you put one there */
button.primary:not(:disabled):hover { background: var(--accent-deep); border-color: var(--accent-deep); }

.btn--secondary { color: var(--fg); background: var(--panel); border-color: var(--line-strong); }
.btn--secondary:not(:disabled):hover { background: var(--bg); }

.btn--ghost { color: var(--fg); background: transparent; border-color: transparent; }
.btn--ghost:not(:disabled):hover { background: rgba(6, 122, 111, .08); }

.btn--danger { color: #fff; background: var(--err); border-color: var(--err); }
.btn--danger:not(:disabled):hover { background: #a03f2c; border-color: #a03f2c; }

.btn--sm, .btn.small { font-size: var(--t-sm); padding: 5px var(--s-3); min-height: 28px; }
.btn--block { width: 100%; }

/* LEGACY ALIASES. The surfaces already say `btn small`, `btn ghost` and
 * `btn danger` in dozens of call sites, in HTML and in DOM-building JS.
 * Aliasing them onto the variants converts every one of those to the shared
 * component without a rename sweep — which is the change most likely to miss
 * a string built at runtime and leave a button unstyled in a state nobody
 * screenshots. */
.btn.ghost { color: var(--fg); background: var(--panel); border-color: var(--line-strong); }
.btn.ghost:not(:disabled):hover { background: var(--bg); border-color: var(--line-strong); }
.btn.danger { color: #fff; background: var(--err); border-color: var(--err); }
.btn.danger:not(:disabled):hover { background: #a03f2c; border-color: #a03f2c; }

/* the auth pages' one button keeps its full-width shape */
button.primary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%; margin-top: var(--s-5); font: inherit; font-size: var(--t-md);
  font-weight: var(--w-medium); line-height: 1.2; cursor: pointer;
  border: 1px solid var(--accent); border-radius: var(--r-md);
  padding: 9px var(--s-4); min-height: 36px;
}
button.primary:disabled { opacity: .5; cursor: not-allowed; }
button.primary:focus-visible { outline: none; box-shadow: var(--focus); }
.row-links { display: flex; justify-content: space-between; margin-top: var(--s-4); font-size: var(--t-sm); }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* WCAG 2.5.8: text links in the auth footer rows measured ~19px tall — give them
 * a real hit area (>=24px) without moving the layout. Scoped to the two link
 * rows, not bare `a`, per the "qualify element selectors" CSS rule above. */
.row-links a, .center-link a { display: inline-block; padding: 4px 2px; margin: -4px -2px; min-height: 24px; line-height: 16px; }
.msg { margin-top: var(--s-4); font-size: var(--t-md); border-radius: var(--r-md); padding: 10px var(--s-3); display: none; max-width: var(--measure); }
.msg.show { display: block; }
/* status messages: on dark these were a pale tint of the hue on a dark ground.
 * Inverted — a light wash with the SAME hue as the text, so the color still
 * carries the meaning and the text still clears 4.5:1 against its own wash. */
.msg.err { background: #fdf0ec; border: 1px solid #f2cfc4; color: var(--err); }
.msg.ok { background: #f0fdf4; border: 1px solid #b7e4c7; color: #14532d; }
.msg.info { background: #eff6ff; border: 1px solid #c3dafe; color: #1e40af; }
.hint { font-size: var(--t-xs); color: var(--muted); margin-top: var(--s-1); }
.center-link { text-align: center; margin-top: var(--s-4); font-size: var(--t-sm); }

/* MOBILE, CREDENTIAL PAGES ONLY.
 *
 * Two measured defects at a 375px viewport on packos.ai: inputs rendered at
 * 13px, and the submit button at 36px tall. Under 16px iOS Safari zooms the
 * page on focus — the layout shifts mid-typing and can carry the submit button
 * off screen — and 44px is the Apple HIG touch-target floor.
 *
 * SCOPED TO `body.auth`, WHICH IS ONLY login / register / forgot-password /
 * reset-password / verify-email. This file is linked by seventeen pages,
 * including dam, viewer, vault and the admin surfaces, so a bare `input` or
 * `.btn` rule here is a product-wide change — which is exactly how the
 * checkbox regression documented above happened. A new class beats a broader
 * selector.
 *
 * `--t-md` is deliberately NOT touched: it is the base of the type scale, so
 * raising it would resize every one of those pages. Only these five, only at
 * phone widths, only the ceiling — desktop is byte-identical.
 *
 * Specificity: `body.auth input:not()x3` is (0,4,1) against the base rule's
 * (0,3,1), and `body.auth .google-btn` is (0,2,1) against the page-local
 * `.google-btn` at (0,1,0), so both win on specificity rather than on order. */
@media (max-width: 768px) {
  body.auth input:not([type='checkbox']):not([type='radio']):not([type='range']),
  body.auth select,
  body.auth textarea {
    font-size: 16px;          /* the iOS zoom threshold — not a design choice */
    min-height: 44px;
  }
  /* height only. A 13px label on a 44px button is legible and keeps these
   * pages on the type scale; the zoom trigger is focus on a FIELD, not a tap. */
  body.auth button.primary,
  body.auth .google-btn {
    min-height: 44px;
  }
}
