/* Schwarz Gruppe corporate design for the age-verification showcase (spec §16).
 *
 * One stylesheet for every page, which is why the pages were moved onto shared
 * chrome in S3 before anything here existed. Tokens first, then the few
 * structures the showcase actually needs — there is no component library here
 * and there should not be: eight pages do not earn one.
 *
 * Two rules from the brand are load-bearing rather than cosmetic and are worth
 * naming where they are implemented:
 *
 *   - The palette has no red. Rejections are therefore encoded by surface,
 *     shape and words, never by hue alone (also WCAG 1.4.1).
 *   - Text is left-aligned, mixed case, never all-caps and never italic.
 */

:root {
  --sg-white: #ffffff;
  --sg-blue-1: #c8daff;
  --sg-blue-2: #94b2ff;
  --sg-blue-3: #4a6be8;
  --sg-blue-4: #1f38c2;
  --sg-blue-5: #0a1a4f;
  --sg-lime: #e5ff29;
  --sg-green: #32dc5a;

  --sg-bg: var(--sg-white);
  --sg-text: var(--sg-blue-5);
  --sg-accent: var(--sg-blue-4);

  /* Ink, as inherited values rather than fixed colours.
   *
   * Every dark surface in this stylesheet redefines these two, and everything
   * that writes text reads them. That is not tidiness — it is the fix for a
   * defect that shipped: .small hard-coded blue 5, so on the rejected verdict
   * it rendered blue 5 on blue 5 at a contrast ratio of 1.00:1. Invisible, and
   * invisible in the one place a reader most needs the text.
   *
   * Patching each dark container would have worked until the next one, which
   * is why the value moved instead. */
  --ink: var(--sg-blue-5);
  --ink-muted: var(--sg-blue-4);

  /* Emphasis, for the same reason and by the same mechanism. The brand does
   * not use italic cuts at all, so <em> cannot mean what the browser default
   * makes it mean, and it is used often enough in this prose — 63 times across
   * the templates — that leaving it italic would have been the most repeated
   * brand violation in the build. It is re-cut as colour plus weight, and the
   * colour has to invert on dark ground like --ink does: blue 4 on blue 5 is
   * 1.9:1 and would take the emphasised word out of the sentence entirely. */
  --ink-emph: var(--sg-blue-4);

  /* The page margin is proportional rather than fixed, which is the web
   * reading of the brand's ÷5 rule: the formula is print geometry and there is
   * no short side in a browser, so what is kept is the ratio of logo to margin,
   * not the arithmetic. */
  --page-margin: clamp(1.25rem, 5vw, 6rem);
  --measure: 34rem;

  /* The ratios matter more than the absolute values, and one of them was too
   * tight: h3 against body text sat at 1.18, which reads as emphasis rather
   * than as a level. Widened to 1.24, where the step is legible while
   * scanning — which is what the long pages need. Weight and family already
   * carry their share, so the size does not have to shout. */
  --step--1: 0.875rem;
  --step-0: 1.0625rem;
  --step-1: 1.3125rem;
  --step-2: 1.75rem;
  --step-3: clamp(2rem, 5vw, 2.5rem);
  --step-4: clamp(2.5rem, 8vw, 4rem);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--sg-bg);
  color: var(--sg-text);
  font-family: "Helvetica Now Text", "Helvetica Now Display", "Helvetica Neue",
    Arial, system-ui, sans-serif;
  font-size: var(--step-0);
  line-height: 1.55;
  text-align: left;
}

/* --- Grid -----------------------------------------------------------------
 * Five columns, because the brand asks for an odd count and five is the one
 * that still means something at this page width. Text sits in the first three;
 * surfaces are allowed to break out, which is what the brand's "Farbflächen
 * dürfen ausbrechen" means in practice. */

.page {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0 2rem;
  padding: 0 var(--page-margin) 4rem;
  max-width: 84rem;
  margin: 0 auto;
}

.page > * {
  grid-column: 1 / 4;
  min-width: 0;
}

.page > .full {
  grid-column: 1 / -1;
}

@media (max-width: 60rem) {
  .page {
    display: block;
    padding-bottom: 3rem;
  }
}

/* --- Masthead -------------------------------------------------------------
 * The logo sits in a corner with a white anchor to the page edge, and keeps
 * clear space of at least a quarter of its own height. Both are brand rules
 * with numbers attached, so they are expressed as numbers. */

.masthead {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding: 1.5rem 0 1.25rem;
  margin-bottom: 2rem;
  flex-wrap: wrap;
  /* The upper of two rules that frame the page. It is the same weight and the
   * same colour as the one above the footer on purpose: together they close
   * the chrome around the argument, and a reader who scrolls past the second
   * one has seen the first. A single rule would have read as decoration. */
  border-bottom: 2px solid var(--sg-blue-5);
}

.masthead img {
  display: block;
  height: 44px;
  width: auto;
  /* A quarter of the logo height, kept as a ratio so that changing the height
   * above cannot silently violate the clear space. */
  margin: 11px 11px 11px 0;
}

.masthead .where {
  font-size: var(--step--1);
  color: var(--sg-blue-4);
  padding-top: 1rem;
  margin: 0;
}

/* The utility navigation, between the logo and the where-label. It is a third
 * item in a space-between row, which is what puts it in the middle without a
 * position rule. Sized down to the caption step: these are the pages a reader
 * goes to when they want more, never the reason they came. */
.masthead .pages {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding-top: 1rem;
  font-size: var(--step--1);
}

/* The page a reader is already on stops being an offer. Colour alone would not
 * carry it (WCAG 1.4.1), so the underline goes and the weight changes with
 * it — and aria-current says the same thing to a screen reader, which is the
 * only reason this is not merely cosmetic. */
.masthead .pages a[aria-current="page"] {
  color: var(--sg-blue-5);
  font-weight: 500;
  text-decoration: none;
  cursor: default;
}

/* --- The two doors --------------------------------------------------------
 * Navigation that says what the showcase is about rather than where its pages
 * are. The marks repeat the data shadow at small size, so the difference
 * between the doors is legible before either page is opened. */

.doors {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
  max-width: 46rem;
}

.doors a {
  display: block;
  border: 1px solid var(--sg-blue-1);
  border-left: 4px solid var(--sg-blue-4);
  border-radius: 2px;
  padding: 0.7rem 0.9rem;
  text-decoration: none;
  color: var(--sg-blue-5);
}

.doors a:hover {
  background: var(--sg-blue-1);
}

.doors .door {
  display: block;
  font-weight: 500;
  font-size: var(--step-0);
}

.doors .what {
  display: block;
  font-size: var(--step--1);
  color: var(--sg-blue-4);
}

@media (max-width: 40rem) {
  .doors {
    grid-template-columns: 1fr;
  }
}

/* --- Stage label ----------------------------------------------------------
 * On every page (§9). Deliberately not styled down into a footnote: it says
 * this is a throwaway showcase with test credentials, and a visitor who
 * arrives from somebody else's screenshot has to be able to read it. */

.label {
  --ink: var(--sg-white);
  --ink-muted: var(--sg-blue-2);
  --ink-emph: var(--sg-blue-2);
  grid-column: 1 / -1;
  background: var(--sg-blue-5);
  color: var(--sg-white);
  padding: 0.9rem 1.1rem;
  font-size: var(--step--1);
  line-height: 1.5;
  border-radius: 2px;
}

.label a {
  color: var(--sg-blue-2);
}

/* --- Type ----------------------------------------------------------------- */

h1,
h2,
h3 {
  font-family: "Helvetica Now Display", "Helvetica Now Text", "Helvetica Neue",
    Arial, sans-serif;
  color: var(--sg-blue-5);
  line-height: 1.15;
  margin: 2.5rem 0 0.75rem;
  text-wrap: balance;
}

h1 {
  font-size: var(--step-3);
  font-weight: 500;
}

h2 {
  font-size: var(--step-2);
  font-weight: 500;
}

h3 {
  font-size: var(--step-1);
  font-weight: 500;
}

p {
  margin: 0 0 1rem;
  max-width: var(--measure);
}

.lede {
  font-size: var(--step-1);
  color: var(--ink);
}

.small {
  font-size: var(--step--1);
  color: var(--ink);
  opacity: 0.85;
}

.eyebrow {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin: 0 0 0.25rem;
  font-weight: 500;
}

/* No italics anywhere (brand rule, and the reason --ink-emph exists). The
 * emphasis still has to be visible, so it is carried by weight and colour —
 * which also keeps <em> and <strong> apart: emphasis shifts hue, strong stays
 * in the text colour and gets heavier. Two distinct signals rather than two
 * neighbouring weights nobody can tell apart mid-sentence. */
em {
  font-style: normal;
  font-weight: 500;
  color: var(--ink-emph);
}

a {
  color: var(--sg-blue-4);
  text-underline-offset: 0.2em;
}

a:hover {
  color: var(--sg-blue-5);
}

code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.9em;
  background: var(--sg-blue-1);
  color: var(--sg-blue-5);
  padding: 0.1em 0.35em;
  border-radius: 2px;
  /* Claim names and origins are long, unhyphenated and set in monospace. A
   * browser will not break `https://age-check.dev.sit.svc.odj.cloud` at a dot,
   * so on a phone it pushes the whole layout sideways — and the origin is
   * printed on the station page, which is the page most likely to be opened on
   * a phone. Breaking inside the token is the lesser evil. */
  overflow-wrap: anywhere;
}

/* --- Controls ------------------------------------------------------------- */

button {
  font: inherit;
  font-weight: 500;
  color: var(--sg-blue-5);
  background: var(--sg-lime);
  border: 2px solid var(--sg-blue-5);
  border-radius: 2px;
  padding: 0.7rem 1.4rem;
  margin: 1rem 1rem 0 0;
  cursor: pointer;
}

button:hover:not(:disabled) {
  background: var(--sg-blue-5);
  color: var(--sg-white);
}

button:disabled {
  background: var(--sg-blue-1);
  border-color: var(--sg-blue-3);
  color: var(--sg-blue-5);
  opacity: 0.7;
  cursor: default;
}

/* Secondary: the attack buttons. They are not the call to action of the page
 * and must not compete with it, but they are also not disabled-looking — they
 * do something, and something interesting. */
button.quiet {
  background: var(--sg-white);
  border-color: var(--sg-blue-4);
  color: var(--sg-blue-4);
}

button.quiet:hover:not(:disabled) {
  background: var(--sg-blue-4);
  color: var(--sg-white);
}

:focus-visible {
  outline: 3px solid var(--sg-blue-4);
  outline-offset: 2px;
}

/* --- Tables --------------------------------------------------------------- */

table {
  border-collapse: collapse;
  margin: 1rem 0;
  font-size: var(--step--1);
  width: 100%;
}

th,
td {
  border-bottom: 1px solid var(--sg-blue-1);
  padding: 0.5rem 0.75rem 0.5rem 0;
  text-align: left;
  vertical-align: top;
}

th {
  font-weight: 500;
  color: var(--sg-blue-4);
  border-bottom-color: var(--sg-blue-3);
}

/* The §4 checklist. Three columns, and the middle one carries the sentences,
 * so it gets the room — the letters are an index, not the content. */
table.steps td:first-child {
  width: 16rem;
  padding-right: 1.5rem;
}

table.steps td:last-child {
  width: 8rem;
  white-space: nowrap;
}

@media (max-width: 60rem) {
  table.steps td:first-child,
  table.steps td:last-child {
    width: auto;
  }
}

/* --- The data shadow ------------------------------------------------------
 * The showcase's argument is about *quantity*: one bit at the first door, a
 * name at the second. That is a shape rather than a sentence, so it is drawn.
 *
 * Same mark, two sizes, on both doors and side by side on the evidence view.
 * Nothing here is decorative — if the marks ever stop matching what the door
 * actually asks for, the page is lying, which is why they sit next to the
 * claim list rather than in a hero. */

.shadow,
.mark {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  --mark-size: 1.1rem;
}

.shadow {
  margin: 1.25rem 0;
}

/* The second size the comment above promises. It rides in the door navigation,
 * where there is no room for the full mark and no need for one: at this size it
 * is not read, it is recognised — and by the time a visitor meets the full mark
 * on the page below, they have already seen its shape. */
.mark {
  --mark-size: 0.65rem;
  gap: 0.35rem;
  margin-top: 0.4rem;
}

/* Both marks stay scoped to their two containers rather than becoming free
 * class names. `.wide` is already taken: the senior tile is `.tile.wide`, and
 * an unscoped `.wide` would hand it this rule's height and filled ground —
 * `.tile` declares neither, so nothing would have overridden them. The bug
 * would have shown up as the one tile that carries the uncomfortable half of
 * the argument collapsing to a 1.1rem blue bar. */
.shadow .cell,
.mark .cell {
  width: var(--mark-size);
  height: var(--mark-size);
  border: 2px solid var(--sg-blue-4);
  border-radius: 2px;
  background: var(--sg-white);
  flex: none;
}

.shadow .cell.on,
.mark .cell.on {
  background: var(--sg-blue-4);
}

.shadow .wide,
.mark .wide {
  min-width: 7rem;
  height: var(--mark-size);
  border: 2px solid var(--sg-blue-4);
  border-radius: 2px;
  background: var(--sg-blue-4);
  flex: none;
}

/* In the door the bar may not keep its 7rem floor — it would set the width of
 * the navigation. It stays proportionally long, which is the whole point of
 * the comparison, but relative to the door rather than to the page. */
.mark .wide {
  min-width: 0;
  width: 4.5rem;
}

.shadow .what {
  font-size: var(--step--1);
  color: var(--sg-blue-5);
}

/* --- Tiles ----------------------------------------------------------------
 * §14.2: several tiles rather than one product, so that the check is visibly
 * *selective* — not every purchase asks, and that is shown rather than said.
 *
 * The senior tile spans the row. It is not a product like the others: it is
 * where the demonstration stops being reassuring, and giving it the same box
 * as the wine would file it as one more item on a shelf. */

.tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem;
  margin: 2rem 0;
}

.tile {
  border: 1px solid var(--sg-blue-1);
  border-top: 4px solid var(--sg-blue-4);
  border-radius: 2px;
  padding: 1.25rem;
  min-width: 0;
}

.tile > h2:first-child {
  margin-top: 0;
}

.tile.wide {
  grid-column: 1 / -1;
  border-top-color: var(--sg-blue-5);
}

.two-up {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem;
  margin: 1rem 0 1.5rem;
}

/* The two questions of the senior tile are told apart by a rule on the side,
 * not by a colour: one is answered, the other cannot be. */
.two-up > div {
  border-left: 3px solid var(--sg-blue-3);
  padding-left: 1rem;
}

.two-up > div:last-child {
  border-left-style: dashed;
}

@media (max-width: 60rem) {
  .tiles,
  .two-up {
    grid-template-columns: 1fr;
  }
}

/* --- Verdicts -------------------------------------------------------------
 * Three states, and the palette has no red, so none of them is told by colour
 * alone (§16, WCAG 1.4.1):
 *
 *   yes       white surface, green rule, filled mark
 *   no        blue 1 surface, same mark, empty
 *   rejected  inverted surface, hatched, no mark at all
 *
 * "No" and "rejected" must not resemble each other. That is §4g — "not asked"
 * and "asked and denied" may never produce the same result — carried into the
 * layout, where an audience reads it faster than any sentence. */

.verdict {
  grid-column: 1 / -1;
  margin: 1.5rem 0;
  padding: 1.5rem;
  border: 2px solid var(--sg-blue-5);
  border-radius: 2px;
  max-width: 46rem;
}

.verdict .headline {
  font-family: "Helvetica Now Display", "Helvetica Neue", Arial, sans-serif;
  font-size: var(--step-4);
  font-weight: 500;
  line-height: 1.05;
  margin: 0;
}

/* The green rule is reinforcement, never the message.
 *
 * Measured: SG green on white is 1.82:1, well under the 3:1 that WCAG 1.4.11
 * asks of a graphical object carrying meaning. Green is the brand's success
 * colour and stays, but it may not be load-bearing at that ratio — so the
 * verdict is carried by the words and by the filled mark, and the stripe is
 * bounded by the box's own blue-5 border (green against blue 5 measures
 * 9.05:1) rather than floating against white. Anyone reading this in
 * black-and-white, or not distinguishing the hue at all, loses nothing. */
.verdict.yes {
  background: var(--sg-white);
  border-left: 10px solid var(--sg-green);
  box-shadow: inset 10px 0 0 -8px var(--sg-blue-5);
}

.verdict.no {
  background: var(--sg-blue-1);
  border-left: 10px solid var(--sg-blue-4);
}

.verdict.rejected {
  --ink: var(--sg-white);
  --ink-muted: var(--sg-blue-2);
  --ink-emph: var(--sg-blue-2);
  background: var(--sg-blue-5);
  color: var(--sg-white);
  border-color: var(--sg-blue-5);
  border-left: 10px solid var(--sg-blue-2);
  /* The hatch is the second, non-colour code. It reads as "something was
   * turned away" rather than "the answer was no", and it survives a
   * black-and-white projector, which a hue does not. */
  background-image: repeating-linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.07) 0 8px,
    rgba(255, 255, 255, 0) 8px 16px
  );
}

.verdict.rejected code {
  background: rgba(255, 255, 255, 0.14);
  color: var(--sg-white);
}

.verdict.rejected a {
  color: var(--sg-blue-2);
}

/* --- Panels --------------------------------------------------------------- */

.panel {
  border: 1px solid var(--sg-blue-1);
  border-left: 4px solid var(--sg-blue-3);
  border-radius: 2px;
  padding: 1.1rem 1.25rem;
  margin: 1.5rem 0;
  background: var(--sg-white);
}

.panel h2,
.panel h3 {
  margin-top: 0;
}

/* The error stage. Inverted, because it is the part of the page where the
 * service is being attacked rather than used, and the change of ground is the
 * fastest way to say so from the back of a room. */
.stage-dark {
  --ink: var(--sg-white);
  --ink-muted: var(--sg-blue-2);
  --ink-emph: var(--sg-blue-2);
  grid-column: 1 / -1;
  background: var(--sg-blue-5);
  color: var(--sg-white);
  padding: 2rem;
  border-radius: 2px;
  margin: 2.5rem 0;
  position: relative;
  overflow: hidden;
  max-width: 60rem;
}

.stage-dark h2,
.stage-dark h3 {
  color: var(--sg-white);
}

.stage-dark code {
  background: rgba(255, 255, 255, 0.14);
  color: var(--sg-white);
}

.stage-dark button {
  background: transparent;
  border-color: var(--sg-blue-2);
  color: var(--sg-white);
}

.stage-dark button:hover:not(:disabled) {
  background: var(--sg-white);
  color: var(--sg-blue-5);
}

.stage-dark :focus-visible {
  outline-color: var(--sg-lime);
}

/* The Powerloop: background key visual, one instance, one size, low opacity.
 * It frames the message and never competes with it — and it appears on the
 * dark stage only, so that the brand's "not in different sizes within one
 * layout" rule cannot be broken by a second placement. */
.stage-dark::before {
  content: "";
  position: absolute;
  inset: -20% -10% auto auto;
  width: 30rem;
  height: 30rem;
  background: url("/assets/brand/powerloop-white.svg") right top / contain
    no-repeat;
  opacity: 0.1;
  pointer-events: none;
}

.stage-dark > * {
  position: relative;
}

/* --- The contrast --------------------------------------------------------
 * §15 says it outright: both stores on one screen, and the contrast is the
 * content of the page rather than its decoration. So they sit side by side and
 * stay side by side for as long as the viewport allows — this is the one place
 * in the showcase where stacking two things vertically would lose the argument
 * rather than merely reflow it.
 *
 * The two halves are told apart by surface and by a label, not by hue: the
 * personal side is the one with a filled ground, and it says so in words. */

.contrast {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
  margin: 2rem 0;
  align-items: start;
}

/* Blue 3 rather than blue 1 for these two boxes, and only for these two.
 * Elsewhere a blue-1 hairline is decoration — a table rule the reader does not
 * need, because the columns already separate the content. Here the box *is*
 * the argument: two stores, side by side, and where one ends and the other
 * begins is the thing being shown. A boundary that carries meaning owes 3:1
 * under WCAG 1.4.11, and blue 1 on white measures 1.41. Blue 3 measures 4.61.
 * The filled ground on .personal stays the primary signal; this only makes the
 * quiet half's edge visible at all. */
.contrast > * {
  border: 1px solid var(--sg-blue-3);
  border-radius: 2px;
  padding: 1.25rem;
  min-width: 0;
  overflow-x: auto;
}

/* The half that deliberately knows who. Filled ground, so the difference is
 * visible before either heading is read. */
.contrast > .personal {
  background: var(--sg-blue-1);
  border-color: var(--sg-blue-4);
}

h2.named::after {
  content: " · personenbezogen";
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--sg-blue-4);
}

@media (max-width: 60rem) {
  .contrast {
    grid-template-columns: 1fr;
  }
}

/* --- Figures --------------------------------------------------------------
 * The diagrams are SVG and therefore vector: they scale to the column and stay
 * sharp at any zoom, which is what a reader does with a sequence diagram. The
 * scroll container is for narrow screens, where a diagram that forced the page
 * to scroll sideways would take the whole layout with it. */

figure {
  margin: 1.5rem 0 2rem;
  max-width: 60rem;
  overflow-x: auto;
}

figure img {
  display: block;
  max-width: 100%;
  height: auto;
  background: var(--sg-white);
}

figcaption {
  margin-top: 0.75rem;
  max-width: var(--measure);
}

/* --- Status lines --------------------------------------------------------- */

#status,
#attack-status {
  margin-top: 1rem;
  white-space: pre-wrap;
  max-width: var(--measure);
}

#countdown {
  font-variant-numeric: tabular-nums;
}

/* --- Footer --------------------------------------------------------------- */

footer {
  grid-column: 1 / -1;
  margin-top: 3.5rem;
  /* The lower of the two framing rules. Its twin closes the masthead. */
  border-top: 2px solid var(--sg-blue-5);
  padding-top: 1.5rem;
  font-size: var(--step--1);
}

.ways {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem 2.5rem;
  max-width: 46rem;
}

.ways p {
  margin: 0.2rem 0 0;
}

/* Sized a step up from the sentence under it so the destination reads as the
 * heading of its own paragraph. It is not an h-level: the footer is not part
 * of the document outline, and giving it headings would put "Wie es
 * funktioniert" into the table of contents of every page. */
a.way {
  font-size: var(--step-0);
  font-weight: 500;
}

.legal {
  margin: 2rem 0 0;
  color: var(--ink-muted);
}

.legal a {
  margin-right: 1.25rem;
}

@media (max-width: 40rem) {
  .ways {
    grid-template-columns: 1fr;
  }
}

address {
  font-style: normal;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
