@layer tb.tokens, tb.fonts, tb.base, tb.icons, tb.components, tb.utilities;
@import url("https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&display=swap") layer(tb.fonts);
/* Tunnelbana — a design system with the manners of the Stockholm metro.
 *
 * One file to inject:
 *
 *   <link rel="stylesheet" href="tunnelbana.css">
 *
 * Everything is wrapped in @layer, so any style the host app already has
 * wins by default. Nothing here can quietly take over a page it was
 * dropped into.
 *
 * Drop any line below that you do not want — the rest keeps working:
 *   fonts       Barlow from Google Fonts. Skip it to self-host, or to let
 *               the Helvetica/Arial fallback stand.
 *   base        element defaults. Skip it in an app that owns its own.
 *   utilities   six layout helpers. Skip it and use your own.
 */

/* Tunnelbana — fonts.
 * Optional. Skip this file and the stack falls back to Helvetica Neue /
 * Arial, which is what SL's own site falls back to, so nothing looks wrong.
 * Barlow is here because SL Gothic is licensed and undistributable, and
 * Barlow's bold is the closest free match to a T-Centralen sign.
 */

/* Tunnelbana — tokens.
 * The one owner of every value in the system. Nothing else in this repo
 * hardcodes a colour, size or duration; components only read from here.
 *
 * Provenance: the palette, radii, stroke widths and spacing steps are SL's
 * own published values, read from the design tokens shipped on sl.se
 * (--color-*, --border-radius-*, --space-*). Where SL's system has no value
 * for something a dark UI needs, the value is derived here, never invented
 * twice.
 */
@layer tb.tokens {
  :root {
    /* ── Greys ──────────────────────────────────────────────────────────
     * SL's neutral ramp. 700/950 are added steps a dark UI needs.        */
    --tb-grey-0:    #ffffff;
    --tb-grey-50:   #f1f2f3;
    --tb-grey-100:  #d8dadc;
    --tb-grey-200:  #c1c2c6;
    --tb-grey-300:  #8a8e93;
    --tb-grey-400:  #73777d;
    --tb-grey-500:  #676c72;
    --tb-grey-600:  #4c5157;
    --tb-grey-700:  #363b42;
    --tb-grey-800:  #20252c;
    --tb-grey-900:  #12151a;
    --tb-grey-950:  #0a0c0f;
    --tb-black:     #000000;

    /* ── Signage accents ────────────────────────────────────────────────
     * Sky is the pictogram tile. Yellow is the second language, and by
     * extension every "read this too" on a sign.                         */
    --tb-sky:          #92cff3;
    --tb-sky-mid:      #29a3e0;
    --tb-blue:         #007bb5;
    --tb-blue-deep:    #006699;
    --tb-blue-night:   #0d405e;
    --tb-blue-pale:    #ecf6fe;
    --tb-yellow:       #f9c623;
    --tb-yellow-soft:  #f7dd5f;
    --tb-yellow-pale:  #fdf6d8;

    /* ── Lines ──────────────────────────────────────────────────────────
     * SL's real line colours, tuned by SL for white. The dark theme lifts
     * them by --tb-line-lift; the value below stays the single owner.     */
    --tb-brand-red:            #d71d24;  /* Röda linjen        13 14      */
    --tb-brand-green:          #148541;  /* Gröna linjen       17 18 19   */
    --tb-brand-blue:           #007db8;  /* Blå linjen         10 11      */
    --tb-brand-pink:           #cc417f;  /* Pendeltåg          40–48      */
    --tb-brand-orange:         #a36b00;  /* Tvärbanan          30 31      */
    --tb-brand-rust:           #b75220;  /* Lidingöbanan       21         */
    --tb-brand-slate:          #627892;  /* Nockebybanan       12         */
    --tb-brand-purple:         #9f599a;  /* Roslagsbanan       27 28 29   */
    --tb-brand-teal:           #028387;  /* Saltsjöbanan       25 26      */
    --tb-brand-stone:          #747770;  /* Spårväg City       7          */

    /* ── Feedback ───────────────────────────────────────────────────────*/
    --tb-brand-success:       #1d9f64;
    --tb-brand-success-deep:  #197e50;
    --tb-brand-success-pale:  #e2f3eb;
    --tb-brand-warning:       #fb9e53;
    --tb-brand-warning-deep:  #d07432;
    --tb-brand-warning-pale:  #fff0e5;
    --tb-brand-danger:        #f05555;
    --tb-brand-danger-deep:   #ce3333;
    --tb-brand-danger-pale:   #fdecec;

    /* ── Type ───────────────────────────────────────────────────────────
     * SL Gothic is licensed, not distributable. Barlow is the closest free
     * face — checked side by side against a T-Centralen sign. The last two
     * are SL's own fallbacks, so an unstyled load still looks like SL.     */
    --tb-font-display: "SL Gothic Display", "SL Gothic", Barlow, "Helvetica Neue", Arial, sans-serif;
    --tb-font-text:    "SL Gothic Text", "SL Gothic", Barlow, "Helvetica Neue", Arial, sans-serif;
    --tb-font-mono:    ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;

    --tb-size-xs:   0.75rem;   /* 12 */
    --tb-size-sm:   0.875rem;  /* 14 */
    --tb-size-md:   1rem;      /* 16 */
    --tb-size-lg:   1.125rem;  /* 18 */
    --tb-size-xl:   1.375rem;  /* 22 */
    --tb-size-2xl:  1.75rem;   /* 28 */
    --tb-size-3xl:  2.25rem;   /* 36 */
    --tb-size-4xl:  3rem;      /* 48 */
    --tb-size-5xl:  4rem;      /* 64 */

    --tb-lh-flat:   1;
    --tb-lh-tight:  1.15;
    --tb-lh-snug:   1.3;
    --tb-lh-normal: 1.5;

    --tb-weight-regular:  400;
    --tb-weight-medium:   500;
    --tb-weight-semibold: 600;
    --tb-weight-bold:     700;

    --tb-track-tight:  -0.01em;
    --tb-track-normal: 0;
    --tb-track-caps:   0.04em;

    /* ── Space ─────────────────────────────────────────────────────────
     * 4px base. SL's own steps are 4 8 16 24 32 40 48 64 72 80.          */
    --tb-space-1:  0.25rem;
    --tb-space-2:  0.5rem;
    --tb-space-3:  0.75rem;
    --tb-space-4:  1rem;
    --tb-space-6:  1.5rem;
    --tb-space-8:  2rem;
    --tb-space-10: 2.5rem;
    --tb-space-12: 3rem;
    --tb-space-16: 4rem;
    --tb-space-18: 4.5rem;
    --tb-space-20: 5rem;

    /* ── Shape ──────────────────────────────────────────────────────────
     * SL rounds everything by 4px and nothing more. The pictogram tile is
     * the exception: its radius scales with the tile, as on a real sign.  */
    --tb-radius-none: 0;
    --tb-radius-sm:   2px;
    --tb-radius-md:   4px;
    --tb-radius-lg:   8px;
    --tb-radius-pill: 999px;
    --tb-radius-tile: 0.22em;

    --tb-stroke-thin:   1px;
    --tb-stroke:        2px;
    --tb-stroke-thick:  4px;
    --tb-stroke-stripe: 8px;

    /* ── Rails ──────────────────────────────────────────────────────────*/
    --tb-rail-width:  var(--tb-stroke-stripe);
    --tb-station:     14px;
    --tb-interchange: 22px;

    /* ── Motion ─────────────────────────────────────────────────────────*/
    --tb-ease:      cubic-bezier(0.2, 0.8, 0.2, 1);
    --tb-dur-fast:  120ms;
    --tb-dur:       200ms;
    --tb-dur-slow:  320ms;

    /* ── Elevation ──────────────────────────────────────────────────────
     * SL casts shadows down-right only, never centred.                   */
    --tb-shadow-raised:  2px 2px 4px 0 var(--tb-shadow-ink);
    --tb-shadow-overlay: 3px 3px 12px 0 var(--tb-shadow-ink);
    --tb-shadow-modal:   6px 6px 16px 0 var(--tb-shadow-ink);

    --tb-z-sticky: 10;
    --tb-z-overlay: 100;

    /* How much white the dark theme lifts a line colour by. Checked
     * against the platform maps: a 16% lift lands on the printed colour. */
    --tb-line-lift: 16%;
  }

  /* ── Dark: the default, because signage is dark ──────────────────────*/
  :root {
    color-scheme: dark;

    --tb-surface-page:   var(--tb-grey-950);
    --tb-surface-sign:   var(--tb-grey-900);
    --tb-surface-raised: var(--tb-grey-800);
    --tb-surface-hover:  var(--tb-grey-700);

    --tb-border-faint:  var(--tb-grey-800);
    --tb-border:        var(--tb-grey-700);
    --tb-border-strong: var(--tb-grey-500);

    --tb-text:        var(--tb-grey-0);
    --tb-text-soft:   var(--tb-grey-200);
    --tb-text-muted:  var(--tb-grey-300);
    --tb-text-second: var(--tb-yellow);
    --tb-text-on-accent: var(--tb-grey-950);

    --tb-accent:       var(--tb-sky-mid);
    --tb-accent-text:  var(--tb-sky);
    --tb-accent-quiet: var(--tb-blue-night);

    --tb-pict-bg: var(--tb-sky);
    --tb-pict-fg: var(--tb-grey-900);

    --tb-focus:      var(--tb-yellow);
    --tb-shadow-ink: #00000073;

    --tb-line-red:    color-mix(in oklab, var(--tb-brand-red)     , white var(--tb-line-lift));
    --tb-line-green:  color-mix(in oklab, var(--tb-brand-green)   , white var(--tb-line-lift));
    --tb-line-blue:   color-mix(in oklab, var(--tb-brand-blue)    , white var(--tb-line-lift));
    --tb-line-pink:   color-mix(in oklab, var(--tb-brand-pink)    , white var(--tb-line-lift));
    --tb-line-orange: color-mix(in oklab, var(--tb-brand-orange)  , white var(--tb-line-lift));
    --tb-line-rust:   color-mix(in oklab, var(--tb-brand-rust)    , white var(--tb-line-lift));
    --tb-line-slate:  color-mix(in oklab, var(--tb-brand-slate)   , white var(--tb-line-lift));
    --tb-line-purple: color-mix(in oklab, var(--tb-brand-purple)  , white var(--tb-line-lift));
    --tb-line-teal:   color-mix(in oklab, var(--tb-brand-teal)    , white var(--tb-line-lift));
    --tb-line-stone:  color-mix(in oklab, var(--tb-brand-stone)   , white var(--tb-line-lift));

    --tb-ok:        var(--tb-brand-success);
    --tb-ok-quiet:  color-mix(in oklab, var(--tb-brand-success), var(--tb-surface-page) 78%);
    --tb-warn:      var(--tb-brand-warning);
    --tb-warn-quiet: color-mix(in oklab, var(--tb-brand-warning), var(--tb-surface-page) 80%);
    --tb-danger:    var(--tb-brand-danger);
    --tb-danger-quiet: color-mix(in oklab, var(--tb-brand-danger), var(--tb-surface-page) 80%);
    --tb-note:      var(--tb-yellow);
    --tb-note-quiet: color-mix(in oklab, var(--tb-yellow), var(--tb-surface-page) 82%);
  }

  /* ── Light: the printed map, not the lit sign ───────────────────────
   * Two things have to move, not just invert. Yellow-on-white is
   * unreadable, so the second language becomes deep blue; and the line
   * colours drop back to SL's own white-background values.              */
  :root[data-tb-theme="light"] {
    color-scheme: light;

    --tb-surface-page:   var(--tb-grey-50);
    --tb-surface-sign:   var(--tb-grey-0);
    --tb-surface-raised: var(--tb-grey-50);
    --tb-surface-hover:  var(--tb-grey-100);

    --tb-border-faint:  var(--tb-grey-100);
    --tb-border:        var(--tb-grey-200);
    --tb-border-strong: var(--tb-grey-400);

    --tb-text:        var(--tb-grey-900);
    --tb-text-soft:   var(--tb-grey-600);
    --tb-text-muted:  var(--tb-grey-400);
    --tb-text-second: var(--tb-blue-deep);
    --tb-text-on-accent: var(--tb-grey-0);

    --tb-accent:       var(--tb-blue);
    --tb-accent-text:  var(--tb-blue-deep);
    --tb-accent-quiet: var(--tb-blue-pale);

    --tb-focus:      var(--tb-blue-deep);
    --tb-shadow-ink: #00000026;

    --tb-line-red:    var(--tb-brand-red);
    --tb-line-green:  var(--tb-brand-green);
    --tb-line-blue:   var(--tb-brand-blue);
    --tb-line-pink:   var(--tb-brand-pink);
    --tb-line-orange: var(--tb-brand-orange);
    --tb-line-rust:   var(--tb-brand-rust);
    --tb-line-slate:  var(--tb-brand-slate);
    --tb-line-purple: var(--tb-brand-purple);
    --tb-line-teal:   var(--tb-brand-teal);
    --tb-line-stone:  var(--tb-brand-stone);

    --tb-ok:           var(--tb-brand-success-deep);
    --tb-ok-quiet:     var(--tb-brand-success-pale);
    --tb-warn:         var(--tb-brand-warning-deep);
    --tb-warn-quiet:   var(--tb-brand-warning-pale);
    --tb-danger:       var(--tb-brand-danger-deep);
    --tb-danger-quiet: var(--tb-brand-danger-pale);
    --tb-note:         var(--tb-brand-warning-deep);
    --tb-note-quiet:   var(--tb-yellow-pale);
  }
}

/* Tunnelbana — base.
 * Element defaults. Optional: drop this file when injecting into an app
 * that already owns its element styles, and the components still work.
 */
@layer tb.base {
  *,
  *::before,
  *::after { box-sizing: border-box; }

  html {
    -webkit-text-size-adjust: 100%;
    scrollbar-color: var(--tb-border) transparent;
  }

  body {
    margin: 0;
    background: var(--tb-surface-page);
    color: var(--tb-text);
    font-family: var(--tb-font-text);
    font-size: var(--tb-size-md);
    font-weight: var(--tb-weight-medium);
    line-height: var(--tb-lh-normal);
    -webkit-font-smoothing: antialiased;
  }

  /* Signs are bold. Headings are the signs of a page.                   */
  h1, h2, h3, h4, h5, h6 {
    margin: 0;
    font-family: var(--tb-font-display);
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    letter-spacing: var(--tb-track-tight);
    text-wrap: balance;
  }

  h1 { font-size: var(--tb-size-4xl); }
  h2 { font-size: var(--tb-size-3xl); }
  h3 { font-size: var(--tb-size-2xl); }
  h4 { font-size: var(--tb-size-xl); }
  h5 { font-size: var(--tb-size-lg); }
  h6 { font-size: var(--tb-size-md); }

  p, ul, ol, dl, figure, pre, blockquote { margin: 0; }
  p { text-wrap: pretty; }

  a {
    color: var(--tb-accent-text);
    text-decoration-thickness: var(--tb-stroke);
    text-underline-offset: 0.18em;
  }
  a:hover { color: var(--tb-text); }

  strong, b { font-weight: var(--tb-weight-bold); }

  code, kbd, samp, pre {
    font-family: var(--tb-font-mono);
    font-size: 0.9em;
  }

  hr {
    border: 0;
    border-block-start: var(--tb-stroke) solid var(--tb-border);
    margin-block: var(--tb-space-6);
  }

  img, svg, video, canvas { max-inline-size: 100%; block-size: auto; }

  ::selection {
    background: var(--tb-yellow);
    color: var(--tb-grey-950);
  }

  /* One focus ring for the whole system, and it is loud on purpose.     */
  :focus-visible {
    outline: var(--tb-stroke) solid var(--tb-focus);
    outline-offset: var(--tb-stroke);
  }

  table { border-collapse: collapse; }

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

/* Tunnelbana — icons.
 * Drawn on a 24px grid in the SL signage manner: one solid silhouette,
 * no outlines, no detail that survives being read at ten metres.
 *
 * Each icon is a mask, so it takes its colour from `color` like text does
 * and needs no build step, no sprite and no file paths. That is the whole
 * reason they live in CSS: one file to inject, nothing to resolve.
 *
 *   <span class="tb-icon" data-icon="metro"></span>
 *   <span class="tb-pict" data-icon="train"></span>
 */
@layer tb.icons {
  :root {
    --tb-icon-metro:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 1.6a10.4 10.4 0 1 0 0 20.8 10.4 10.4 0 0 0 0-20.8zm0 2.6a7.8 7.8 0 1 1 0 15.6 7.8 7.8 0 0 1 0-15.6z'/%3E%3Cpath d='M7.1 7.2h9.8v2.6h-3.6v7.4h-2.6V9.8H7.1z'/%3E%3C/svg%3E");
    --tb-icon-train:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 1.6c4.6 0 8.3 3.7 8.3 8.3v6.6a3.5 3.5 0 0 1-3.5 3.5H7.2a3.5 3.5 0 0 1-3.5-3.5V9.9c0-4.6 3.7-8.3 8.3-8.3zm-5.4 6.9h10.8v4.4H6.6zm.3 6.7h3.4v2.2H6.9zm6.8 0h3.4v2.2h-3.4z'/%3E%3Cpath d='M6.6 21h3.6v1.6H6.6zm7.2 0h3.6v1.6h-3.6z'/%3E%3C/svg%3E");
    --tb-icon-tram:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M11.2 0h1.6v3.6h-1.6z'/%3E%3Cpath fill-rule='evenodd' d='M7.4 3.4h9.2a1.8 1.8 0 0 1 1.8 1.8v14a1.8 1.8 0 0 1-1.8 1.8H7.4a1.8 1.8 0 0 1-1.8-1.8v-14a1.8 1.8 0 0 1 1.8-1.8zm.4 3h8.4v5.4H7.8zm.2 8h2.8v2.2H8zm5.8 0h2.8v2.2h-2.8z'/%3E%3C/svg%3E");
    --tb-icon-bus:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M4.5 3h15a2 2 0 0 1 2 2v10.5a2 2 0 0 1-2 2h-15a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm.6 3.4h5.6v4.8H5.1zm8.2 0h5.6v4.8h-5.6z'/%3E%3Cpath d='M7 17a2.6 2.6 0 1 0 0 5.2A2.6 2.6 0 0 0 7 17zm10 0a2.6 2.6 0 1 0 0 5.2A2.6 2.6 0 0 0 17 17z'/%3E%3C/svg%3E");
    --tb-icon-ferry:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M11.2 1.4h1.6v3.6h-1.6z'/%3E%3Cpath d='M7.5 5h9v6.6h-9z'/%3E%3Cpath d='M2 13.2h20l-2.7 7A2.5 2.5 0 0 1 17 21.8H7a2.5 2.5 0 0 1-2.3-1.6z'/%3E%3C/svg%3E");
    --tb-icon-plane:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c.9 0 1.6.7 1.6 1.6v6.2l8.4 3.1v2.3l-8.4-2v4.3l2.6 1.9v1.6L12 20l-4.2.9v-1.6l2.6-1.9v-4.3l-8.4 2v-2.3l8.4-3.1V3.6C10.4 2.7 11.1 2 12 2z'/%3E%3C/svg%3E");
    --tb-icon-bike:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.6 19.4a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2zm12.8 0a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2z'/%3E%3Cpath d='m5.6 15.8 4-8h4.2l4.6 8m-8.8 0h7.6M13.4 7.8h3.2'/%3E%3C/svg%3E");
    --tb-icon-walk:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 5.6a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4z'/%3E%3Cpath d='M10.8 6.6h2.4v7h-2.4zM8 8l2.6 1.2v2.4L7 10.2zm8 0-2.6 1.2v2.4L17 10.2zm-5.2 5.2-3 8.2 2.2.8 2.6-6.4zm2.4 0 3 8.2-2.2.8-2.6-6.4z'/%3E%3C/svg%3E");
    --tb-icon-exit:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2.8h4.2A2.6 2.6 0 0 1 20.8 5.4v13.2a2.6 2.6 0 0 1-2.6 2.6H14'/%3E%3Cpath d='M14.4 12H3.4m0 0 4.4-4.4M3.4 12l4.4 4.4'/%3E%3C/svg%3E");
    --tb-icon-info:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M9.6 2.6h4.8v4.8H9.6zm0 7.2h4.8V21.4H9.6z'/%3E%3C/svg%3E");
    --tb-icon-toilet:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8.1 5.8a2.1 2.1 0 1 0 0-4.2 2.1 2.1 0 0 0 0 4.2z'/%3E%3Cpath d='M6.1 6.8h4v6h-4zm.2 6.4h1.5v8H6.3zm2.1 0h1.5v8H8.4z'/%3E%3Cpath d='M15.9 5.8a2.1 2.1 0 1 0 0-4.2 2.1 2.1 0 0 0 0 4.2z'/%3E%3Cpath d='M15.9 6.6l3.5 8.2h-7z'/%3E%3Cpath d='M14.7 15h1.4v6.2h-1.4zm2.8 0h1.4v6.2h-1.4z'/%3E%3C/svg%3E");
    --tb-icon-accessible: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14.2 5.2a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8z'/%3E%3Cpath fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M13.6 7.4 12.4 13h5.4l2.4 5.8M6.4 11.8A6.6 6.6 0 1 0 16.8 19'/%3E%3C/svg%3E");
    --tb-icon-meeting:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 8.4 8.6 3.4h6.8zM12 15.6l3.4 5H8.6zM8.4 12l-5 3.4V8.6zM15.6 12l5-3.4v6.8z'/%3E%3Cpath d='M12 10.3a1.7 1.7 0 1 0 0 3.4 1.7 1.7 0 0 0 0-3.4z'/%3E%3C/svg%3E");
    --tb-icon-escalator: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round' d='M3.2 20.6h4.2L18.4 9.4'/%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' d='M6.8 20.4 9.2 18m1.6-1.6 2.4-2.4m1.6-1.6 2.4-2.4'/%3E%3Cpath d='M14.8 2.6h6.6v6.6z'/%3E%3C/svg%3E");
    --tb-icon-stairs:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M3.4 21.6V17h4.2v-4.2h4.2V8.6h4.2V4.4h4.6v17.2z'/%3E%3C/svg%3E");
    --tb-icon-lift:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M4.6 2.4h14.8a2.2 2.2 0 0 1 2.2 2.2v14.8a2.2 2.2 0 0 1-2.2 2.2H4.6a2.2 2.2 0 0 1-2.2-2.2V4.6a2.2 2.2 0 0 1 2.2-2.2zm4.6 3.8L6.6 10.6h5.2zm5.6 11.6 2.6-4.4h-5.2z'/%3E%3C/svg%3E");
    --tb-icon-ticket:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M2.6 6h18.8v3.4a2.6 2.6 0 0 0 0 5.2V18H2.6v-3.4a2.6 2.6 0 0 0 0-5.2zm4.6 3.4h9.6v1.8H7.2zm0 3.6h9.6v1.8H7.2z'/%3E%3C/svg%3E");
    --tb-icon-clock:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.2a8.8 8.8 0 1 0 0 17.6 8.8 8.8 0 0 0 0-17.6zm0 3.9V12l3.6 2.4'/%3E%3C/svg%3E");
    --tb-icon-arrow:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12.6 2.2 23 12l-10.4 9.8v-6H1V8.2h11.6z'/%3E%3C/svg%3E");
    --tb-icon-chevron:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m8.6 4.4 7.6 7.6-7.6 7.6'/%3E%3C/svg%3E");
    --tb-icon-check:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m3.8 12.6 5.6 5.6L20.4 6.2'/%3E%3C/svg%3E");
    --tb-icon-cross:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round'%3E%3Cpath d='M5.4 5.4l13.2 13.2M18.6 5.4 5.4 18.6'/%3E%3C/svg%3E");
    --tb-icon-alert:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 1.8 23.4 21.6H.6zm-1.3 6.6h2.6v6.8h-2.6zm0 8.6h2.6v2.6h-2.6z'/%3E%3C/svg%3E");
    --tb-icon-here:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 7.6a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8z'/%3E%3Cpath fill-rule='evenodd' d='M12 1.8a10.2 10.2 0 1 0 0 20.4 10.2 10.2 0 0 0 0-20.4zm0 2.6a7.6 7.6 0 1 1 0 15.2 7.6 7.6 0 0 1 0-15.2z'/%3E%3C/svg%3E");
    --tb-icon-search:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M10.4 2.8a7.6 7.6 0 1 0 0 15.2 7.6 7.6 0 0 0 0-15.2zm5.7 13.1 5.1 5.1'/%3E%3C/svg%3E");
    --tb-icon-terminal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.6 6.6 5.4 5.4-5.4 5.4M12.8 17.4h6.6'/%3E%3C/svg%3E");
    --tb-icon-play:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M6.4 3.4 20.6 12 6.4 20.6z'/%3E%3C/svg%3E");
    --tb-icon-refresh:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' d='M20.4 12a8.4 8.4 0 1 1-2.6-6.1'/%3E%3Cpath d='M21.8 3v6.6h-6.6z'/%3E%3C/svg%3E");
    --tb-icon-menu:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M3 5h18v2.8H3zm0 5.6h18v2.8H3zm0 5.6h18V19H3z'/%3E%3C/svg%3E");
  }

  .tb-icon,
  .tb-pict::before {
    -webkit-mask-image: var(--tb-icon);
    mask-image: var(--tb-icon);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
  }

  .tb-icon {
    display: inline-block;
    flex: none;
    inline-size: 1em;
    block-size: 1em;
    background-color: currentColor;
    vertical-align: -0.125em;
    rotate: var(--tb-icon-rotate, 0deg);
  }

  /* Direction lives on the element, not in a second icon.               */
  .tb-icon[data-dir="up"]         { --tb-icon-rotate: -90deg; }
  .tb-icon[data-dir="down"]       { --tb-icon-rotate: 90deg; }
  .tb-icon[data-dir="left"]       { --tb-icon-rotate: 180deg; }
  .tb-icon[data-dir="up-right"]   { --tb-icon-rotate: -45deg; }
  .tb-icon[data-dir="up-left"]    { --tb-icon-rotate: -135deg; }
  .tb-icon[data-dir="down-right"] { --tb-icon-rotate: 45deg; }
  .tb-icon[data-dir="down-left"]  { --tb-icon-rotate: 135deg; }

  [data-icon="metro"]      { --tb-icon: var(--tb-icon-metro); }
  [data-icon="train"]      { --tb-icon: var(--tb-icon-train); }
  [data-icon="tram"]       { --tb-icon: var(--tb-icon-tram); }
  [data-icon="bus"]        { --tb-icon: var(--tb-icon-bus); }
  [data-icon="ferry"]      { --tb-icon: var(--tb-icon-ferry); }
  [data-icon="plane"]      { --tb-icon: var(--tb-icon-plane); }
  [data-icon="bike"]       { --tb-icon: var(--tb-icon-bike); }
  [data-icon="walk"]       { --tb-icon: var(--tb-icon-walk); }
  [data-icon="exit"]       { --tb-icon: var(--tb-icon-exit); }
  [data-icon="info"]       { --tb-icon: var(--tb-icon-info); }
  [data-icon="toilet"]     { --tb-icon: var(--tb-icon-toilet); }
  [data-icon="accessible"] { --tb-icon: var(--tb-icon-accessible); }
  [data-icon="meeting"]    { --tb-icon: var(--tb-icon-meeting); }
  [data-icon="escalator"]  { --tb-icon: var(--tb-icon-escalator); }
  [data-icon="stairs"]     { --tb-icon: var(--tb-icon-stairs); }
  [data-icon="lift"]       { --tb-icon: var(--tb-icon-lift); }
  [data-icon="ticket"]     { --tb-icon: var(--tb-icon-ticket); }
  [data-icon="clock"]      { --tb-icon: var(--tb-icon-clock); }
  [data-icon="arrow"]      { --tb-icon: var(--tb-icon-arrow); }
  [data-icon="chevron"]    { --tb-icon: var(--tb-icon-chevron); }
  [data-icon="check"]      { --tb-icon: var(--tb-icon-check); }
  [data-icon="cross"]      { --tb-icon: var(--tb-icon-cross); }
  [data-icon="alert"]      { --tb-icon: var(--tb-icon-alert); }
  [data-icon="here"]       { --tb-icon: var(--tb-icon-here); }
  [data-icon="search"]     { --tb-icon: var(--tb-icon-search); }
  [data-icon="terminal"]   { --tb-icon: var(--tb-icon-terminal); }
  [data-icon="play"]       { --tb-icon: var(--tb-icon-play); }
  [data-icon="refresh"]    { --tb-icon: var(--tb-icon-refresh); }
  [data-icon="menu"]       { --tb-icon: var(--tb-icon-menu); }
}

/* Tunnelbana — dials.
 * Two knobs the whole system turns on, so no component needs a variant
 * per colour:
 *
 *   --tb-line   which transport line this thing belongs to
 *   --tb-tone   what this thing is telling you
 *
 * Put a dial class on any component (or set the property yourself) and the
 * badge, rail, stripe, button, banner and status all follow it.
 *
 *   <span class="tb-badge tb-line-red">13</span>
 *   <div class="tb-banner tb-tone-danger">…</div>
 */
@layer tb.components {
  .tb-line-red    { --tb-line: var(--tb-line-red); }
  .tb-line-green  { --tb-line: var(--tb-line-green); }
  .tb-line-blue   { --tb-line: var(--tb-line-blue); }
  .tb-line-pink   { --tb-line: var(--tb-line-pink); }
  .tb-line-orange { --tb-line: var(--tb-line-orange); }
  .tb-line-rust   { --tb-line: var(--tb-line-rust); }
  .tb-line-slate  { --tb-line: var(--tb-line-slate); }
  .tb-line-purple { --tb-line: var(--tb-line-purple); }
  .tb-line-teal   { --tb-line: var(--tb-line-teal); }
  .tb-line-stone  { --tb-line: var(--tb-line-stone); }

  .tb-tone-ok      { --tb-tone: var(--tb-ok);         --tb-tone-quiet: var(--tb-ok-quiet); }
  .tb-tone-warn    { --tb-tone: var(--tb-warn);       --tb-tone-quiet: var(--tb-warn-quiet); }
  .tb-tone-danger  { --tb-tone: var(--tb-danger);     --tb-tone-quiet: var(--tb-danger-quiet); }
  .tb-tone-note    { --tb-tone: var(--tb-note);       --tb-tone-quiet: var(--tb-note-quiet); }
  .tb-tone-info    { --tb-tone: var(--tb-accent);     --tb-tone-quiet: var(--tb-accent-quiet); }
  .tb-tone-neutral { --tb-tone: var(--tb-text-muted); --tb-tone-quiet: var(--tb-surface-raised); }
}

/* Tunnelbana — badge.
 * The line number on a platform sign: a 4px-cornered block of line colour
 * with a bold white number in it. Pendeltåg numbers are pills on the map,
 * hence --pill.
 */
@layer tb.components {
  .tb-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    min-inline-size: 2em;
    padding: 0.12em 0.36em;
    border: var(--tb-stroke) solid transparent;
    border-radius: var(--tb-radius-md);
    background: var(--tb-line, var(--tb-accent));
    color: var(--tb-grey-0);
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-md);
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    letter-spacing: var(--tb-track-normal);
    font-variant-numeric: tabular-nums;
    text-decoration: none;
  }

  .tb-badge--sm { font-size: var(--tb-size-sm); }
  .tb-badge--lg { font-size: var(--tb-size-xl); }

  .tb-badge--pill {
    border-radius: var(--tb-radius-pill);
    padding-inline: 0.55em;
  }

  .tb-badge--outline {
    background: transparent;
    border-color: var(--tb-line, var(--tb-accent));
    color: var(--tb-line, var(--tb-accent));
  }

  /* A group of numbers sharing one destination, as on the map legend.   */
  .tb-badges {
    display: inline-flex;
    gap: var(--tb-space-1);
    align-items: center;
  }
}

/* Tunnelbana — pictogram.
 * The pale blue tile with a dark glyph in it. Everything about it scales
 * from one knob, --tb-pict-size, exactly as the printed tiles do: corner
 * radius and glyph are fractions of the tile, never fixed pixels.
 */
@layer tb.components {
  .tb-pict {
    display: inline-grid;
    place-items: center;
    flex: none;
    font-size: var(--tb-pict-size, 2.5rem);
    inline-size: 1em;
    block-size: 1em;
    border-radius: var(--tb-radius-tile);
    background: var(--tb-pict-bg);
  }

  .tb-pict::before {
    content: "";
    inline-size: 0.74em;
    block-size: 0.74em;
    background-color: var(--tb-pict-fg);
  }

  /* No tile — the glyph alone, in whatever colour it inherits.          */
  .tb-pict--plain { background: none; }
  .tb-pict--plain::before { background-color: currentColor; }

  /* Tile in line colour, glyph knocked out white.                      */
  .tb-pict--line { background: var(--tb-line, var(--tb-accent)); }
  .tb-pict--line::before { background-color: var(--tb-grey-0); }

  .tb-pict--tone { background: var(--tb-tone, var(--tb-accent)); }
  .tb-pict--tone::before { background-color: var(--tb-grey-950); }
}

/* Tunnelbana — sign.
 * The one idea worth stealing from SL: two languages, same size, same
 * weight, told apart by colour alone. White is what you are looking at,
 * yellow is the same thing said again. In an app the yellow line is the
 * gloss — the English, the plain-words version, the value under a label.
 *
 * The whole block scales from its own font-size, so --sm and --lg are the
 * only sizes you ever need.
 */
@layer tb.components {
  .tb-sign {
    display: grid;
    gap: var(--tb-space-6);
    padding: var(--tb-space-8);
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-sign);
    font-size: var(--tb-size-2xl);
  }

  .tb-sign--sm { font-size: var(--tb-size-lg); padding: var(--tb-space-4); gap: var(--tb-space-4); }
  .tb-sign--lg { font-size: var(--tb-size-4xl); }
  .tb-sign--bare { padding: 0; background: none; }

  .tb-sign__row {
    display: flex;
    align-items: center;
    gap: 0.6em;
  }

  .tb-sign__arrow {
    font-size: 1.15em;
    color: var(--tb-text);
  }

  .tb-sign__row .tb-pict { --tb-pict-size: 1.5em; }

  .tb-sign__label { display: grid; }

  .tb-sign__title {
    font-family: var(--tb-font-display);
    font-size: 1em;
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    letter-spacing: var(--tb-track-tight);
    color: var(--tb-text);
  }

  .tb-sign__sub {
    font-family: var(--tb-font-display);
    font-size: 1em;
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    letter-spacing: var(--tb-track-tight);
    color: var(--tb-text-second);
  }

  /* A terminus and the lines that reach it, as on the legend.           */
  .tb-dest {
    display: flex;
    align-items: center;
    gap: var(--tb-space-3);
  }

  .tb-dest__name {
    font-family: var(--tb-font-display);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    color: var(--tb-text);
  }

  .tb-dest__sub {
    font-weight: var(--tb-weight-medium);
    color: var(--tb-text-muted);
  }
}

/* Tunnelbana — rail.
 * A line with stations on it: the map's own way of showing a sequence and
 * where you are in it. Steps, a pipeline, a timeline, a deploy — same
 * thing. The rail takes its colour from --tb-line and ends in a rounded
 * cap at the terminus, like the printed map does.
 *
 *   <ol class="tb-rail tb-line-green">
 *     <li class="tb-station" data-state="done">Alvik</li>
 *     <li class="tb-station" data-state="active">T-Centralen</li>
 *     <li class="tb-station">Slussen</li>
 *   </ol>
 */
@layer tb.components {
  .tb-rail {
    --tb-rail-color: var(--tb-line, var(--tb-accent));
    position: relative;
    display: grid;
    gap: var(--tb-space-6);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .tb-rail::before {
    content: "";
    position: absolute;
    inset-block: 0.35em;
    inset-inline-start: calc((var(--tb-interchange) - var(--tb-rail-width)) / 2);
    inline-size: var(--tb-rail-width);
    border-radius: var(--tb-radius-pill);
    background: var(--tb-rail-color);
  }

  .tb-station {
    position: relative;
    min-block-size: var(--tb-interchange);
    padding-inline-start: calc(var(--tb-interchange) + var(--tb-space-4));
  }

  /* Station dot: white on the rail, as on every SL map.                 */
  .tb-station::before {
    content: "";
    position: absolute;
    inset-inline-start: calc((var(--tb-interchange) - var(--tb-station)) / 2);
    inset-block-start: calc(0.8em - var(--tb-station) / 2);
    inline-size: var(--tb-station);
    block-size: var(--tb-station);
    border-radius: var(--tb-radius-pill);
    background: var(--tb-grey-0);
  }

  /* An interchange is drawn larger and ringed, so it reads as a place
   * you can change at rather than one you pass through.                 */
  .tb-station--interchange::before {
    inset-inline-start: 0;
    inset-block-start: calc(0.8em - var(--tb-interchange) / 2);
    inline-size: var(--tb-interchange);
    block-size: var(--tb-interchange);
    box-shadow: 0 0 0 var(--tb-stroke) var(--tb-grey-500);
  }

  .tb-station[data-state="todo"]::before {
    background: var(--tb-surface-page);
    box-shadow: 0 0 0 var(--tb-stroke) var(--tb-grey-0);
  }

  .tb-station[data-state="failed"]::before { background: var(--tb-danger); }

  /* You are here. The map marks it in yellow and so does this.          */
  .tb-station[data-state="active"]::before {
    background: var(--tb-yellow);
    box-shadow: 0 0 0 var(--tb-stroke) var(--tb-surface-page),
                0 0 0 calc(var(--tb-stroke) * 3) var(--tb-yellow);
  }

  .tb-station[data-state="active"] .tb-station__name { color: var(--tb-yellow); }

  .tb-station__name {
    display: block;
    font-family: var(--tb-font-display);
    font-weight: var(--tb-weight-bold);
    font-size: var(--tb-size-lg);
    line-height: var(--tb-lh-snug);
    color: var(--tb-text);
  }

  .tb-station__meta {
    display: block;
    font-size: var(--tb-size-sm);
    color: var(--tb-text-muted);
  }

  .tb-station--terminus .tb-station__name {
    text-transform: uppercase;
    letter-spacing: var(--tb-track-caps);
  }

  /* Horizontal: the same rail turned, labels under the dots.            */
  .tb-rail--across {
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
  }

  .tb-rail--across::before {
    inset-block: auto;
    inset-inline: 0;
    inset-block-start: calc(0.8em - var(--tb-rail-width) / 2);
    inline-size: auto;
    block-size: var(--tb-rail-width);
  }

  .tb-rail--across .tb-station {
    padding-inline: var(--tb-space-2);
    padding-block-start: calc(0.8em + var(--tb-interchange) / 2 + var(--tb-space-3));
    text-align: center;
  }

  .tb-rail--across .tb-station::before {
    inset-inline-start: calc(50% - var(--tb-station) / 2);
  }

  .tb-rail--across .tb-station--interchange::before {
    inset-inline-start: calc(50% - var(--tb-interchange) / 2);
  }

  /* A bare rail with no stations: a progress bar in line colour.        */
  .tb-meter {
    --tb-meter-value: 0;
    block-size: var(--tb-rail-width);
    border-radius: var(--tb-radius-pill);
    background: var(--tb-surface-raised);
    overflow: hidden;
  }

  .tb-meter::before {
    content: "";
    display: block;
    block-size: 100%;
    inline-size: calc(var(--tb-meter-value) * 1%);
    border-radius: inherit;
    background: var(--tb-line, var(--tb-accent));
    transition: inline-size var(--tb-dur-slow) var(--tb-ease);
  }
}

/* Tunnelbana — status.
 * A dot and a word, in the tone you set. Reads at a glance from across a
 * room, which is the only test a status indicator has to pass.
 */
@layer tb.components {
  .tb-status {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    padding: 0.2em 0.6em 0.2em 0.5em;
    border-radius: var(--tb-radius-md);
    background: var(--tb-tone-quiet, var(--tb-surface-raised));
    color: var(--tb-tone, var(--tb-text-muted));
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-sm);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    white-space: nowrap;
  }

  .tb-status::before {
    content: "";
    flex: none;
    inline-size: 0.6em;
    block-size: 0.6em;
    border-radius: var(--tb-radius-pill);
    background: currentColor;
  }

  /* Working on it. The only thing in the system that moves.             */
  .tb-status--live::before { animation: tb-pulse 1.4s var(--tb-ease) infinite; }

  @keyframes tb-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.25; }
  }

  .tb-dot {
    display: inline-block;
    inline-size: 0.7em;
    block-size: 0.7em;
    border-radius: var(--tb-radius-pill);
    background: var(--tb-tone, var(--tb-line, var(--tb-text-muted)));
    vertical-align: 0.05em;
  }

  .tb-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    padding: 0.15em 0.5em;
    border: var(--tb-stroke-thin) solid var(--tb-border);
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-raised);
    color: var(--tb-text-soft);
    font-size: var(--tb-size-sm);
    font-weight: var(--tb-weight-semibold);
  }
}

/* Tunnelbana — button.
 * 4px corners, 2px border, bold label. Hover lightens rather than moves:
 * signs do not animate.
 */
@layer tb.components {
  .tb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
    padding: 0.5em 1.1em;
    border: var(--tb-stroke) solid transparent;
    border-radius: var(--tb-radius-md);
    background: var(--tb-accent);
    color: var(--tb-text-on-accent);
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-md);
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    text-decoration: none;
    cursor: pointer;
    transition: background var(--tb-dur-fast) var(--tb-ease),
                border-color var(--tb-dur-fast) var(--tb-ease),
                color var(--tb-dur-fast) var(--tb-ease);
  }

  .tb-btn:hover { background: color-mix(in oklab, var(--tb-accent), white 18%); }
  .tb-btn:active { background: color-mix(in oklab, var(--tb-accent), black 12%); }

  .tb-btn--secondary {
    background: transparent;
    border-color: var(--tb-border-strong);
    color: var(--tb-text);
  }
  .tb-btn--secondary:hover {
    background: var(--tb-surface-raised);
    border-color: var(--tb-text);
  }

  .tb-btn--ghost {
    background: transparent;
    color: var(--tb-text-soft);
  }
  .tb-btn--ghost:hover {
    background: var(--tb-surface-raised);
    color: var(--tb-text);
  }

  /* Takes whichever dial you turned: a tone for consequence, a line for
   * which part of the system it belongs to.                             */
  .tb-btn--tone {
    background: var(--tb-tone);
    color: var(--tb-grey-950);
  }
  .tb-btn--tone:hover { background: color-mix(in oklab, var(--tb-tone), white 18%); }

  .tb-btn--line {
    background: var(--tb-line);
    color: var(--tb-grey-0);
  }
  .tb-btn--line:hover { background: color-mix(in oklab, var(--tb-line), white 18%); }

  .tb-btn--sm { padding: 0.3em 0.7em; font-size: var(--tb-size-sm); }
  .tb-btn--lg { padding: 0.6em 1.4em; font-size: var(--tb-size-xl); }
  .tb-btn--block { inline-size: 100%; }

  .tb-btn:disabled,
  .tb-btn[aria-disabled="true"] {
    background: var(--tb-surface-raised);
    border-color: transparent;
    color: var(--tb-text-muted);
    cursor: not-allowed;
  }

  .tb-btns {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tb-space-3);
    align-items: center;
  }
}

/* Tunnelbana — card.
 * A panel is a sign hung on the page. The 8px stripe down its edge is
 * SL's own device for saying which line this belongs to; turn --tb-line
 * or --tb-tone and the stripe follows.
 */
@layer tb.components {
  .tb-card {
    display: grid;
    gap: var(--tb-space-4);
    padding: var(--tb-space-6);
    border: var(--tb-stroke-thin) solid var(--tb-border-faint);
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-sign);
  }

  .tb-card--stripe {
    border-inline-start: var(--tb-stroke-stripe) solid var(--tb-line, var(--tb-tone, var(--tb-accent)));
  }

  .tb-card--raised { box-shadow: var(--tb-shadow-raised); }

  .tb-card--quiet {
    background: var(--tb-surface-raised);
    border-color: transparent;
  }

  .tb-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tb-space-4);
  }

  .tb-card__title {
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-lg);
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    color: var(--tb-text);
  }

  .tb-card__meta {
    font-size: var(--tb-size-sm);
    color: var(--tb-text-muted);
  }

  /* One number, said loudly. The label under it is the yellow gloss.    */
  .tb-figure {
    display: grid;
    gap: var(--tb-space-1);
  }

  .tb-figure__value {
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-4xl);
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-flat);
    letter-spacing: var(--tb-track-tight);
    font-variant-numeric: tabular-nums;
    color: var(--tb-tone, var(--tb-text));
  }

  .tb-figure__label {
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-sm);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    color: var(--tb-text-second);
  }
}

/* Tunnelbana — banner.
 * Trafikinformation. A stripe, a pictogram, a heading in the tone colour
 * and the detail in plain white underneath.
 */
@layer tb.components {
  .tb-banner {
    display: flex;
    align-items: flex-start;
    gap: var(--tb-space-4);
    padding: var(--tb-space-4) var(--tb-space-6);
    border-inline-start: var(--tb-stroke-stripe) solid var(--tb-tone, var(--tb-note));
    border-radius: var(--tb-radius-md);
    background: var(--tb-tone-quiet, var(--tb-note-quiet));
    color: var(--tb-text);
  }

  .tb-banner .tb-icon {
    flex: none;
    font-size: var(--tb-size-xl);
    color: var(--tb-tone, var(--tb-note));
  }

  .tb-banner__body { display: grid; gap: var(--tb-space-1); }

  .tb-banner__title {
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-md);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    color: var(--tb-tone, var(--tb-note));
  }

  .tb-banner__text {
    font-size: var(--tb-size-md);
    color: var(--tb-text-soft);
  }
}

/* Tunnelbana — field.
 * 2px borders because that is what SL uses, and a yellow focus ring
 * because on a sign yellow means look here.
 */
@layer tb.components {
  .tb-field {
    display: grid;
    gap: var(--tb-space-2);
  }

  .tb-field__label {
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-sm);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    color: var(--tb-text-second);
  }

  .tb-field__hint {
    font-size: var(--tb-size-sm);
    color: var(--tb-text-muted);
  }

  .tb-input,
  .tb-select,
  .tb-textarea {
    inline-size: 100%;
    padding: 0.55em 0.7em;
    border: var(--tb-stroke) solid var(--tb-border-strong);
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-raised);
    color: var(--tb-text);
    font-family: var(--tb-font-text);
    font-size: var(--tb-size-md);
    font-weight: var(--tb-weight-medium);
    line-height: var(--tb-lh-snug);
  }

  .tb-input::placeholder,
  .tb-textarea::placeholder { color: var(--tb-text-muted); }

  .tb-input:hover,
  .tb-select:hover,
  .tb-textarea:hover { border-color: var(--tb-text-muted); }

  .tb-textarea {
    min-block-size: 6em;
    resize: vertical;
  }

  /* No custom arrow: the native one already follows color-scheme, and a
   * hand-drawn one would need a second copy of the chevron per theme.   */
  .tb-select { padding-inline-end: var(--tb-space-2); }

  .tb-check {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--tb-space-3);
    font-weight: var(--tb-weight-semibold);
    cursor: pointer;
  }

  .tb-check input {
    appearance: none;
    flex: none;
    inline-size: 1.35em;
    block-size: 1.35em;
    margin: 0;
    border: var(--tb-stroke) solid var(--tb-border-strong);
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-raised);
    cursor: pointer;
  }

  .tb-check input[type="radio"] { border-radius: var(--tb-radius-pill); }

  .tb-check input:checked {
    background: var(--tb-accent);
    border-color: var(--tb-accent);
  }

  .tb-check input[type="radio"]:checked {
    box-shadow: inset 0 0 0 0.25em var(--tb-surface-raised);
  }

  /* The tick is drawn over the box by the label, so it can be coloured
   * from a token instead of baked into the icon.                        */
  .tb-check::before {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    inline-size: 1.35em;
    block-size: 1.35em;
    background-color: var(--tb-text-on-accent);
    -webkit-mask-image: var(--tb-icon-check);
    mask-image: var(--tb-icon-check);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: 0.85em;
    mask-size: 0.85em;
    opacity: 0;
    pointer-events: none;
  }

  .tb-check:has(input[type="checkbox"]:checked)::before { opacity: 1; }
}

/* Tunnelbana — table.
 * A timetable: no zebra stripes, one firm rule under the head, numbers
 * that line up in columns.
 */
@layer tb.components {
  .tb-table {
    inline-size: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
  }

  .tb-table th {
    padding: var(--tb-space-2) var(--tb-space-3);
    border-block-end: var(--tb-stroke) solid var(--tb-border-strong);
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-sm);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    text-align: start;
    color: var(--tb-text-second);
    white-space: nowrap;
  }

  .tb-table td {
    padding: var(--tb-space-3);
    border-block-end: var(--tb-stroke-thin) solid var(--tb-border-faint);
    color: var(--tb-text-soft);
    vertical-align: middle;
  }

  .tb-table tbody tr:hover td { background: var(--tb-surface-raised); }

  .tb-table__lead {
    font-family: var(--tb-font-display);
    font-weight: var(--tb-weight-bold);
    color: var(--tb-text);
  }

  .tb-table__num { text-align: end; }

  .tb-table-scroll { overflow-x: auto; }
}

/* Tunnelbana — tabs.
 * The active tab is marked by a 4px rule in line colour, the way a
 * platform sign underlines the line you are standing on.
 */
@layer tb.components {
  .tb-tabs {
    display: flex;
    gap: var(--tb-space-6);
    border-block-end: var(--tb-stroke-thin) solid var(--tb-border);
  }

  .tb-tab {
    padding: var(--tb-space-3) 0;
    border: 0;
    border-block-end: var(--tb-stroke-thick) solid transparent;
    margin-block-end: -1px;
    background: none;
    color: var(--tb-text-muted);
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-md);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: color var(--tb-dur-fast) var(--tb-ease);
  }

  .tb-tab:hover { color: var(--tb-text); }

  .tb-tab[aria-selected="true"],
  .tb-tab[aria-current="page"] {
    border-block-end-color: var(--tb-line, var(--tb-accent));
    color: var(--tb-text);
  }
}

/* Tunnelbana — console.
 * Monospace output surfaces: a log stream, a command, a key. The level
 * colour goes on the gutter, never the message, so a wall of output still
 * reads as a wall of text you can scan down the left edge of.
 */
@layer tb.components {
  .tb-log {
    display: grid;
    padding: var(--tb-space-4);
    border-radius: var(--tb-radius-md);
    border: var(--tb-stroke-thin) solid var(--tb-border-faint);
    background: var(--tb-surface-sign);
    font-family: var(--tb-font-mono);
    font-size: var(--tb-size-sm);
    line-height: var(--tb-lh-snug);
    overflow: auto;
  }

  .tb-log__line {
    display: grid;
    grid-template-columns: max-content max-content 1fr;
    gap: var(--tb-space-4);
    padding: 0.15em 0;
    color: var(--tb-text-soft);
    white-space: pre-wrap;
    word-break: break-word;
  }

  .tb-log__time { color: var(--tb-grey-500); }

  .tb-log__level {
    color: var(--tb-tone, var(--tb-text-muted));
    font-weight: var(--tb-weight-bold);
    text-transform: uppercase;
  }

  .tb-log__line:hover { background: var(--tb-surface-raised); }

  .tb-kbd {
    display: inline-block;
    padding: 0.1em 0.4em;
    border: var(--tb-stroke-thin) solid var(--tb-border);
    border-block-end-width: var(--tb-stroke);
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-raised);
    color: var(--tb-text);
    font-family: var(--tb-font-mono);
    font-size: 0.85em;
    line-height: var(--tb-lh-snug);
  }

  .tb-code {
    padding: 0.1em 0.35em;
    border-radius: var(--tb-radius-sm);
    background: var(--tb-surface-raised);
    color: var(--tb-accent-text);
    font-family: var(--tb-font-mono);
  }
}

/* Tunnelbana — dialog. Works on a bare <dialog>. */
@layer tb.components {
  .tb-dialog {
    max-inline-size: min(34rem, calc(100vw - var(--tb-space-8)));
    padding: var(--tb-space-8);
    border: 0;
    border-radius: var(--tb-radius-md);
    background: var(--tb-surface-sign);
    color: var(--tb-text);
    box-shadow: var(--tb-shadow-modal);
  }

  .tb-dialog::backdrop { background: #000000a6; }

  .tb-dialog__title {
    font-family: var(--tb-font-display);
    font-size: var(--tb-size-2xl);
    font-weight: var(--tb-weight-bold);
    line-height: var(--tb-lh-tight);
    margin-block-end: var(--tb-space-3);
  }

  .tb-dialog__foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--tb-space-3);
    margin-block-start: var(--tb-space-6);
  }
}

/* Tunnelbana — utilities.
 * Deliberately tiny. This is not a utility framework; these are the six
 * things every one of the components needed anyway.
 */
@layer tb.utilities {
  .tb-stack {
    display: grid;
    gap: var(--tb-gap, var(--tb-space-4));
  }

  .tb-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tb-gap, var(--tb-space-4));
  }

  .tb-spread {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tb-gap, var(--tb-space-4));
  }

  .tb-muted { color: var(--tb-text-muted); }
  .tb-second { color: var(--tb-text-second); }

  .tb-caps {
    font-family: var(--tb-font-display);
    font-weight: var(--tb-weight-bold);
    letter-spacing: var(--tb-track-caps);
    text-transform: uppercase;
  }

  .tb-nums { font-variant-numeric: tabular-nums; }

  .tb-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .tb-sr-only {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

