/* =============================================================================
   CLIENT PANEL THEME — id.edge.lt
   Built from the design tokens in your own style.css, so the panel and the
   marketing site read as one product.

   THE CSP IS style-src 'self' (Sec 4.2). No inline styles anywhere, no <style>
   attributes, no font CDN, no @import from a remote origin. Everything the page
   needs is in this file, served from our own host.

   THE HTML STRUCTURE CARRIES THE ACCESSIBILITY (Sec 21) — real landmarks, one h1
   per page, labels tied to inputs, aria-describedby on errors, role="status" on
   the notice. THIS STYLESHEET MUST PRESERVE THAT, never replace it. In
   particular: never remove a focus outline without putting a clearer one back,
   and never let colour be the only carrier of meaning.
   ============================================================================= */

:root {
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
               "Helvetica Neue", Arial, sans-serif;

  --color-bg: #ffffff;
  --color-surface: #fafafa;
  --color-surface-elevated: #ffffff;
  --color-page: #f5f5f7;
  --color-border: #e8e8ed;
  --color-border-hover: #d2d2d7;

  --color-text-primary: #1d1d1f;
  --color-text-secondary: #5f5f64;
  --color-text-muted: #6e6e73;

  --color-accent: #84cc16;
  --color-accent-hover: #65a30d;
  --color-accent-soft: rgba(132, 204, 22, 0.08);

  --btn-primary-bg: #1d1d1f;
  --btn-primary-bg-hover: #000000;
  --btn-primary-text: #ffffff;

/* Two values differ from style.css, both for contrast: --tint-green-600 and
   --tint-orange-600 are darkened for light mode, because the site sets them on
   icon glyphs (3:1) and this panel sets them as status words (4.5:1). Reasoning
   in spec AMENDMENT-08 A4; the list is enforced in tests/Stage3/ThemeTest.php,
   which holds every other shared token identical. */
/* THE STATUS TINTS, AND ONLY THE STATUS TINTS: success, caution, failure. The
   rest of the palette is charcoal (every button) and lime (the focus ring).
   The site's blue and purple are deliberately absent — they are decoration
   there, and a fourth colour with no meaning is how a status palette stops
   being readable. AMENDMENT-08 A1. */
  --tint-green-50: #f0fdf4;  --tint-green-600: #15803d;
  --tint-orange-50: #fff7ed; --tint-orange-600: #c2410c;
  --tint-red-50: #fef2f2;    --tint-red-600: #b91c1c;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.04);
  --shadow-md: 0 4px 16px rgba(0,0,0,.06);

  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 14px;
  --radius-pill: 999px;

  /* --- THE COUNT TILES ------------------------------------------------------
     THE SITE'S CTA COLOURS, PINNED TO THEIR LIGHT-MODE VALUES, and identical to
     admin.css's. Nothing here is a new colour: style.css's own #1d1d1f,
     #000000, #ffffff and its --btn-primary-arrow rgba(255,255,255,0.6).

     COPIED, AND NOT `var(--btn-primary-bg)`, ON PURPOSE. Those tokens INVERT in
     dark mode, so a tile built from them would turn white with dark text
     exactly where it was asked to stay charcoal.

     WRITTEN OUT RATHER THAN SHARED, like the banner below: the two stylesheets
     are separate on purpose and must not import from each other, but the tile a
     client sees and the tile an admin sees are the same component. */
  --card-bg: #1d1d1f;
  --card-bg-hover: #000000;
  --card-text: #ffffff;
  --card-text-dim: rgba(255, 255, 255, 0.6);

  --container-max: 72rem;

  /* LAYOUT AND HEADER TOKENS, LIFTED FROM style.css AT ITS OWN VALUES. (D45)
     The panel wears the marketing header now, and the header's geometry is
     these four numbers — bar height, and the gutter the logo sits behind. They
     are copied rather than re-chosen because the whole point is that the logo
     lands on the same pixel on both sites; a panel that picked its own 1.25rem
     gutter would put it 4px out and nobody would ever find out why.

     THE TOKEN-AGREEMENT TEST NOW COVERS THEM. It checks the INTERSECTION of
     what the two files declare, so from here on style.css cannot move any of
     these without this file going red — which is exactly the guard this
     arrangement needs. */
  --container-pad: 1rem;
  --container-pad-md: 1.5rem;
  --header-h: 4rem;
  --header-h-md: 5rem;
}

/* DARK MODE, matching style.css's own dark block value for value.
   THE TINTS ARE LIFTED, NOT KEPT: a 600 chosen to be legible on white is not
   legible on near-black. This file once flipped the 50-weight BACKGROUNDS and
   left the 600s behind — a half-done inversion whose forgotten half was the one
   used as TEXT, at 3.04:1. AMENDMENT-08 A4. */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0b0b0c;
    --color-surface: #161618;
    --color-surface-elevated: #1d1d20;
    --color-page: #0b0b0c;
    --color-border: #2a2a2e;
    --color-border-hover: #3a3a40;
    --color-text-primary: #f5f5f7;
    --color-text-secondary: #a1a1a6;
    --color-text-muted: #86868b;
    --color-accent: #a3e635;
    --color-accent-hover: #bef264;
    --color-accent-soft: rgba(163, 230, 53, 0.12);
    --btn-primary-bg: #f5f5f7;
    --btn-primary-bg-hover: #ffffff;
    --btn-primary-text: #0b0b0c;
    --tint-green-50: rgba(34,197,94,.14);   --tint-green-600: #4ade80;
    --tint-orange-50: rgba(249,115,22,.14); --tint-orange-600: #fb923c;
    --tint-red-50: rgba(239,68,68,.14);     --tint-red-600: #f87171;

    /* SHADOWS: HEAVIER ON DARK, AS style.css's ARE — its own words, "so
       elevation still reads". This block never had them, so dark mode kept the
       light values, 4-6% black on a near-black page, and every shadow in the
       panel was invisible there while the site's showed (D87d). The token test
       compared only what each block DECLARED, and a token a block leaves out
       is inherited, not compared — that gap is closed in ThemeTest too. */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
    --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.55);
  }
}

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

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

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  /* 1.5 ON THE BODY AND 1.6 ON PARAGRAPHS is style.css's arrangement, not two
     values that drifted: interface chrome wants the tighter one, running prose
     the looser. The panel had 1.6 everywhere, which spaced out labels, table
     cells and nav links that the site sets tighter. */
  line-height: 1.5;
  letter-spacing: normal;
  color: var(--color-text-primary);
  background: var(--color-page);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* --- Skip link: visible the moment it takes focus ------------------------- */
.skip-link {
  position: absolute; left: -9999px; top: 0;
  padding: 12px 20px; z-index: 100;
  background: var(--color-text-primary); color: var(--color-bg);
  border-radius: 0 0 var(--radius-sm) 0; text-decoration: none;
}
.skip-link:focus { left: 0; }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- Focus: never removed, only made clearer -----------------------------
   TWO RINGS, AND THE CHARCOAL ONE CARRIES THE CONTRAST: lime alone is 1.81:1 on
   the page, under the 3:1 WCAG 1.4.11 wants for a focus indicator. THE OFFSET
   AND THE SPREAD ARE BOTH 2px ON PURPOSE — the shadow fills exactly the gap the
   outline leaves; change one and you must change the other. AMENDMENT-08 A6. */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--color-text-primary);
  border-radius: 4px;
}

/* --- Layout --------------------------------------------------------------- */

/* THE CONTAINER IS style.css's, VALUE FOR VALUE. (D45) Everything that has to
   line up with the header logo is measured from this: same max-width, same
   gutter, same 640px breakpoint where the gutter widens. */
.container {
  position: relative;
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}
@media (min-width: 640px) {
  .container { padding-inline: var(--container-pad-md); }
}

/* --- The header, which is the marketing site's (D45) -----------------------

   WHAT THIS REPLACED. A flex bar with a border-bottom and the words "Client
   area" set as a paragraph. It was recognisably a different product from
   edge.lt, one click away from it, at the exact moment a customer is deciding
   whether to type their password in — so the brand mark, the bar height, the
   gutter, the nav type and the hover dot are all style.css's §5 now.

   ── THE THREE PLACES IT DELIBERATELY DIVERGES ──────────────────────────────

   1. position: static, NOT absolute. On edge.lt the header floats over the
      hero, which carries 8rem of top padding to make room for it. The panel has
      no hero, so an absolute header would lie on top of the first thing on
      every page. Static, and first in the body, it lands the logo on the SAME
      pixel anyway — the maths that centres it in a 4rem bar is unchanged.

   2. NO `display: none` ON .site-nav BELOW 768px. That rule is only safe on the
      marketing site because a hamburger takes over there, and the hamburger is
      driven by an inline <script> that this panel cannot have: the CSP is
      script-src 'self' with no unsafe-inline, ThemeTest fails any inline
      script, and the client area ships no JavaScript at all by the decision
      recorded in D32. Copying the rule without the hamburger would leave a
      phone with no navigation whatsoever. So the nav stays visible and wraps.

   3. THE BAR GROWS INSTEAD OF CLIPPING. Consequence of (2): the signed-in
      links will not fit beside the logo on a phone, and a fixed height would
      simply cut them off. min-height plus flex-wrap lets the nav drop to a
      second row. THE LOGO DOES NOT MOVE WHEN THAT HAPPENS — it carries the bar
      height itself, so it stays centred in a 4rem band at the top left whatever
      the nav below it does. That is the whole reason the min-height is on
      .site-logo and not only on the bar; putting it in one place would re-centre
      the logo in a taller bar and shift it down the page. */
.site-header {
  position: static;
  width: 100%;
  z-index: 30;
}
.site-header-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0 2rem;
  min-height: var(--header-h);
}
@media (min-width: 768px) {
  .site-header-bar { min-height: var(--header-h-md); }
}
.site-header-left {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 2rem;
}
.site-logo {
  display: inline-flex;
  align-items: center;
  min-height: var(--header-h);
}
@media (min-width: 768px) {
  .site-logo { min-height: var(--header-h-md); }
}
.site-logo img { max-width: none; display: block; }

.site-nav { display: flex; align-items: center; }
.site-nav-list {
  list-style: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin: 0;
  padding: 0;
}
.site-nav-link {
  position: relative;
  color: var(--color-text-secondary);
  font-size: 14px;
  line-height: 1.3;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-decoration: none;
  transition: color 0.15s ease-in-out;
}
/* THE HOVER COLOUR IS THE PANEL'S, NOT THE SITE'S, AND THIS IS A REAL FIX
   RATHER THAN A COMPROMISE. edge.lt sets `color: var(--color-accent)` on nav
   hover — lime text on the page background, which is 1.81:1 and fails WCAG AA
   for text by a wide margin. This file's own focus-ring comment already worked
   that number out for the same colour. So the hover goes to
   --color-text-primary and the LIME STAYS WHERE IT READS: the dot below the
   link, which is a background on a ::after and answerable to no contrast rule
   because it carries no glyphs. The distinctive part of the marketing hover
   survives; the illegible part does not.

   The marketing site has the same defect on the same links. Worth fixing there
   too, and noted in the D45 upgrade note. */
.site-nav-link:hover,
.site-nav-link[aria-current] { color: var(--color-text-primary); }
.site-nav-link::after {
  content: "";
  position: absolute;
  bottom: -4px;
  left: 50%;
  width: 4px;
  height: 4px;
  border-radius: 999px;
  background: var(--color-accent);
  transform: translateX(-50%) scale(0);
  transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.site-nav-link:hover::after { transform: translateX(-50%) scale(1); }
/* THE CURRENT PAGE KEEPS THE DOT (D94). The colour half of this was written
   when the nav was ported from edge.lt and has been dead ever since: the layout
   emitted no aria-current, so nothing ever matched it. nav_current() now does,
   and the dot comes with it so the two panels and the site all say "you are
   here" the same way.

   THE DOT IS --color-accent, the brand lime, by operator decision — 1.98:1 on
   white, where WCAG 1.4.11 asks 3:1 of a state indicator. It is a SECOND cue
   rather than the only one: the label also moves from --color-text-secondary to
   --color-text-primary, and aria-current carries the state to a screen reader
   whatever the contrast. The radio's checked mark above still uses the darker
   lime, because there the mark is the ONLY thing distinguishing checked from
   unchecked. */
.site-nav-link[aria-current]::after { transform: translateX(-50%) scale(1); }

/* SIGN OUT IS A BUTTON AND HAS TO LOOK LIKE A LINK. It is a POST because it
   changes state, so it cannot be an <a> — but this file's default button is the
   48px accent slab used for form submits, which in a nav row would read as the
   page's primary action. edge.lt's own reset is `background: none; border: 0;
   padding: 0; cursor: pointer`, so that is what it gets.
   NOT SCOPED TO THE NAV ANY MORE (D133): sign out left the page links for the
   far right of the bar, after who is signed in, and a rule still scoped to
   .site-nav-list would have turned it back into the slab. It is the only button
   that carries .site-nav-link. */
button.site-nav-link {
  background: none;
  border: 0;
  padding: 0;
  min-height: 0;
  cursor: pointer;
  font: inherit;
  font-size: 14px;
  font-weight: 400;
}
button.site-nav-link:hover {
  background: none;
  transform: none;
  box-shadow: none;
  color: var(--color-text-primary);
}

/* THE RIGHT-HAND END OF THE BAR (D133): who is signed in, then sign out.
   One flex row, so on a wide screen the pair sits at the right edge (the bar's
   space-between) with sign out last; on a phone the whole block wraps under the
   nav as one row, the address first and sign out after it. The gap is the nav's
   own, so sign out stands as far from the client number as the links stand
   from each other. */
.site-header-right {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 1.5rem;
  min-width: 0;
}
.site-header-logout { display: inline-flex; margin: 0; }

/* WHO IS SIGNED IN, TOP RIGHT (D90): the address. The client number that
   stood beside it is gone from the client area (D143), and its rule with it.
   The bar already spreads its two ends apart (space-between), so this sits at
   the right edge on a wide screen; on a phone it wraps under the nav to its own
   row at the left, with a little room below so it does not touch the page.
   Quieter than the nav — it is information, not somewhere to go. A long
   address breaks rather than widening the header. */
.site-header-account {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.75rem;
  margin: 0;
  padding: 0.25rem 0;
  min-width: 0;
  font-size: 14px;
  line-height: 1.3;
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

main {
  max-width: var(--container-max);
  margin: 0 auto;
  /* THE GUTTER IS THE CONTAINER'S, NOT A FLAT 24px ANY MORE. (D45) It was 24px
     at every width; the header's is 16px below 640px. The logo and the content
     under it were therefore 8px out of line on a phone — the kind of drift that
     reads as sloppiness without ever being identifiable. */
  padding-block: 32px 64px;
  padding-inline: var(--container-pad);
}

.site-footer {
  max-width: var(--container-max);
  margin: 0 auto;
  /* Same gutter as main and the header, for the same reason. */
  padding-block: 0 40px;
  padding-inline: var(--container-pad);
  color: var(--color-text-muted); font-size: 13px;
}
@media (min-width: 640px) {
  main,
  .site-footer { padding-inline: var(--container-pad-md); }
}
.site-footer code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* THE LANGUAGE SWITCHER, in the footer on every page including the logged-out
   ones. Laid out on ONE ROW and sized down, because it is a utility and not a
   call to action: a visitor who needs it goes looking for it, and everybody else
   should be able to ignore it. It wraps rather than overflowing on a phone.
   ON THE RIGHT (D161, the operator's choice), still the footer's first row:
   the place a language control usually sits, and out of the way of the
   reference line under it. Only the alignment moved, so the reading and tab
   order are what they were. */
.lang-switch {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px;
  margin: 0 0 12px;
}
.lang-switch label { font-size: 13px; color: var(--color-text-muted); margin: 0; }
.lang-switch select,
.lang-switch button { font-size: 13px; padding: 6px 12px; width: auto; margin: 0; }

/* --- The signed-out split (D45) --------------------------------------------

   THE RECIPE IS style.css's .pricing-split-grid, NOT A NEW ONE. Same
   mobile-first single column, same 768px break to two equal columns, same
   2rem -> 2.5rem -> 4rem gap ramp, same align-items: center, same
   minmax(0, 1fr) so a long word cannot blow the track out. The panel had no
   two-column layout of its own, and inventing one would have put a shape on
   the login screen that appears nowhere else in the product.

   ONE THING FROM THE RECIPE IS DROPPED: justify-self: end on the text column.
   In the pricing section that pins a paragraph to the right edge against an
   illustration on the left. Here it would push the form to the far right of a
   1152px container and open a canyon down the middle of the page. The columns
   start where their tracks start.

   THE IDENTITY COLUMN IS WHY THIS EXISTS AT ALL. Somebody is about to type a
   password. Next to the box they type it into is who we legally are — the
   registered name, the company code, the country — in the same typeface and on
   the same page furniture as the site they arrived from. Nothing here is
   decoration. */
.auth-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
  align-items: center;
}
@media (min-width: 768px) {
  .auth-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 2.5rem; }
}
@media (min-width: 1024px) {
  .auth-split { gap: 4rem; }
}
.auth-identity { min-width: 0; }
.auth-form { width: 100%; max-width: 28rem; min-width: 0; }

/* SMALL, AND BORROWING SIZES THAT ARE ALREADY IN THIS FILE rather than picking
   new ones: the name is .field label's 14px/500, the detail lines are
   .site-footer's 13px in --color-text-muted. It is a legal footnote sitting
   beside a form, and it should read as one. */
.auth-identity__name {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text-primary);
  margin: 0 0 4px;
}
.auth-identity__line {
  font-size: 13px;
  line-height: 1.5;
  color: var(--color-text-muted);
  margin: 0;
}
/* THE VOICE IS style.css's. The panel had its own — everything at weight 600
   with looser tracking — so a customer arriving from edge.lt met headings that
   were noticeably heavier than the ones they had just been reading.
   THE SIZES ARE NOT COPIED, and that is deliberate: the site's .h1 is
   clamp(2.5rem, 5vw, 3.5rem), a hero on a landing page, and a 56px "Uzsakymas"
   over a form would be absurd. What is copied is the RECIPE at each size — the
   site's own 28px heading (.tile-title) is weight 500 at -0.021em/1.14, and its
   21px card title (.pricing-title) is weight 600 at 0.006em/1.22. Those are the
   two the panel actually needs, so those are the two it uses.

   THE ONE SIZE THAT WAS NOT THE SITE'S WAS THE h1. It read 28px's recipe —
   weight 500, -0.021em, 1.14 — at 30px, a number style.css does not use
   anywhere. The recipe was copied and the size beside it was not, which is the
   hardest kind of divergence to see: everything about the heading is right
   except the one value the comment did not name. It is 28px now, which is
   .domain-copy .tile-title exactly. */
h1 { font-size: 28px; line-height: 1.14; font-weight: 500; letter-spacing: -.021em; margin: 0 0 8px; }
h2 { font-size: 21px; line-height: 1.22; font-weight: 600; letter-spacing: .006em; margin: 32px 0 12px; }
h3 { font-size: 17px; line-height: 1.3; font-weight: 500; letter-spacing: -.019em; margin: 24px 0 8px; }
p  { letter-spacing: -.003em; line-height: 1.6; }
p  { margin: 0 0 16px; }
a  { color: var(--color-text-primary); text-underline-offset: 2px; }

/* --- The universal action notification (Sec 4.4) --------------------------
   Colour is NEVER the only signal: the partial prints the word "Success" or
   "Problem", and the left rule is a second, non-colour cue. */
.notice {
  border: 1px solid var(--color-border);
  border-left-width: 3px;
  border-radius: var(--radius-md);
  padding: 14px 18px;
  margin: 0 0 24px;
  background: var(--color-surface);
}
.notice--success { border-left-color: var(--tint-green-600); background: var(--tint-green-50); }
.notice--failure { border-left-color: var(--tint-red-600);   background: var(--tint-red-50); }
/* NEUTRAL NEWS GETS A NEUTRAL COLOUR. (D74) A notice that is neither good nor
   bad is the page explaining itself, and green or red would both be a claim
   about an outcome there isn't one of. Orange was the other candidate and is
   worse: it reads as a warning, which is a quieter version of the same lie.
   BUILT FROM THE THEME'S OWN TOKENS rather than a new tint, so it follows dark
   mode without a second pair of values to keep in step. */
.notice--info    { border-left-color: var(--color-text-muted); background: var(--color-surface); }
/* INFORMATIONAL, AND DELIBERATELY NEITHER GREEN NOR RED. Carrying a domain over
   from the marketing site is not a success and not a failure; painting it green
   would tell the visitor something completed when nothing has yet been added to
   the cart. Same reasoning as the admin Attention card, which keeps its own
   palette apart from the flash notices for exactly this reason. */
.notice--info { border-left-color: var(--color-text-muted); }
.notice__title { margin: 0; }
.notice__details { margin: 8px 0 0; padding-left: 20px; font-size: 15px; color: var(--color-text-secondary); }

/* --- Forms ---------------------------------------------------------------- */
.field { margin: 0 0 18px; max-width: 30rem; }
.field label { display: block; font-size: 14px; font-weight: 500; color: var(--color-text-secondary); margin-bottom: 6px; }
.field--check { display: flex; align-items: center; gap: 10px; }
.field--check label { margin: 0; font-weight: 400; color: var(--color-text-primary); }
.field--disabled { opacity: .55; }

/* THE FIELD SHAPE IS style.css's .contact-field-wrap, not this file's own. The
   site draws every form field at 14px radius on --color-surface, roughly 52px
   tall, and lights it with an accent border plus a 3px accent glow on focus.
   The panel drew 8px boxes on white at ~42px. THE PILL ON THE SITE'S HOMEPAGE
   SEARCH IS NOT THE GENERAL RULE — that one element is a hero control; the
   contact form is what the site does for ordinary fields, and this is an
   ordinary form.

   48px, NOT 52px: the site's own .domain-search-input is 48 and its buttons are
   48, so a field and the button beside it line up. 52 is the taller variant on
   the domain page, where the input stands alone. */
input[type=text], input[type=email], input[type=password], input[type=number], select, textarea {
  width: 100%;
  min-height: 48px;
  font: inherit;
  font-size: 16px;
  line-height: 1.4;
  color: var(--color-text-primary);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 12px 16px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
textarea { min-height: 140px; resize: vertical; }

/* EVERY ONE OF THESE REPEATS THE TYPE LIST, and it has to. A bare `input:focus`
   also matches RADIOS AND CHECKBOXES — the client area renders nine of each —
   and the accent glow below would then draw a 3px lime halo around a 13px native
   control, on top of the :focus-visible ring it already has, on mouse click as
   well as by keyboard. The shorthand was wrong, not merely untidy. */
input[type=text]::placeholder, input[type=email]::placeholder,
input[type=password]::placeholder, input[type=number]::placeholder,
textarea::placeholder { color: var(--color-text-muted); }

input[type=text]:hover, input[type=email]:hover, input[type=password]:hover,
input[type=number]:hover, select:hover, textarea:hover { border-color: var(--color-border-hover); }

/* The site's :focus-within treatment, applied to the control itself since panel
   fields carry no wrapper element. The panel's own :focus-visible ring still
   applies on top for keyboard users — see the note on it above, which is an
   amendment and stays as it is. */
input[type=text]:focus, input[type=email]:focus, input[type=password]:focus,
input[type=number]:focus, select:focus, textarea:focus {
  border-color: var(--color-accent);
  background: var(--color-bg);
  box-shadow: 0 0 0 3px rgba(132, 204, 22, 0.18);
}
input[type=text][disabled], input[type=email][disabled], input[type=password][disabled],
input[type=number][disabled], select[disabled], textarea[disabled] {
  background: var(--color-surface); cursor: not-allowed;
}

/* A REFUSED FIELD IS MARKED ON THE FIELD ITSELF. (D82)
   The server marks it with aria-invalid, which is what a screen reader hears;
   this is the same fact for the eye. Before, the only mark was the red line of
   text under the box, and the notice above asked people to fix "the highlighted
   fields" when nothing was highlighted.

   AFTER THE :focus RULE ON PURPOSE. Equal specificity, so order decides: the
   refused box keeps its red edge while the cursor is in it — and the page puts
   the cursor there (autofocus), so otherwise the red would never be seen. The
   keyboard ring is an OUTLINE from :focus-visible and this touches no outline.

   A SECOND PIXEL, NOT ONLY A SECOND COLOUR. The inset shadow thickens the edge,
   so the mark is not carried by hue alone (WCAG 1.4.1); the message under the
   box still says what is wrong in words. --tint-red-600 is the token
   .form-error and the failure notice already use, in both modes. */
input[type=text][aria-invalid="true"], input[type=email][aria-invalid="true"],
input[type=password][aria-invalid="true"], input[type=number][aria-invalid="true"],
select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--tint-red-600);
  box-shadow: inset 0 0 0 1px var(--tint-red-600);
}

/* A TICKBOX HAS NO BORDER TO RECOLOUR, so it gets an outline — EXCEPT while it
   holds keyboard focus. :focus-visible draws the focus ring as an outline too,
   and this rule is more specific: without :not() a refused tickbox reached by
   Tab would show red and no focus ring at all (WCAG 2.4.7). */
input[type=checkbox][aria-invalid="true"]:not(:focus-visible) {
  outline: 2px solid var(--tint-red-600);
  outline-offset: 2px;
}

.field__help   { font-size: 13px; color: var(--color-text-muted); margin: 6px 0 0; }
.field__locked { font-size: 13px; color: var(--tint-orange-600); margin: 6px 0 0; }
.form-error    { color: var(--tint-red-600); font-weight: 500; margin: 0 0 16px; }

/* A HELP LINE THAT FOLLOWS A BUTTON IS A BLOCK, NOT A TRAILING PHRASE.
   .field__help is a <p> almost everywhere and an inline <span> in about a dozen
   views, which is right inside the flex rows of .gate and .module__toggle — and
   wrong directly after a submit button in ordinary flow, where the sentence
   starts on the button's own line and wraps underneath it. The client's plan
   upgrade is the clearest: "There is nothing to pay. You have 141 days left..."
   began beside "Upgrade this plan" and read as part of the control.

   SCOPED TO THE ADJACENCY, not to the element. Making every span.field__help a
   block would reach the flex rows too; there it would be harmless — a flex item
   is blockified regardless — but a rule that relies on being ignored somewhere
   is a rule nobody can safely edit later. The margin is already on
   .field__help and only starts applying once this makes it a block. */
button + .field__help { display: block; }


/* --- The radio button: one drawing, the same as edge.lt's (D87e) ----------
   DRAWN BY US, NOT BY THE BROWSER, and declaration for declaration the radio in
   style.css — a test holds the two identical. Tinting the browser's own radio
   (accent-color) only recoloured each browser's drawing; its size, ring and dot
   still differed from Chrome to Safari to Firefox, and from the radio the order
   form draws.

   STILL THE REAL <input>: the keyboard, a screen reader and the form see a radio
   exactly as before. The order form's hidden radios (.visually-hidden) are left
   out — their card is the control, and draws a badge when chosen (D105).
   SINCE D105 NO PAGE SHOWS A RADIO: the last visible pair, on the domain-only
   order, became the order form's cards. This stays as the one drawing for the
   next radio anyone adds, held to edge.lt's by a test.

   RING --color-text-muted (5.0:1 on white), CHECKED MARK --color-accent-hover
   (3.09:1): WCAG 1.4.11 asks 3:1 for a control and its state, and the bright
   accent is 1.98:1. THE MARK IS DRAWN ON THE RADIO'S OWN DISC OF --color-bg:
   on white it is 3.09:1, but on the #f5f5f7 page these rows sit on it would be
   2.84:1. In forced colours (Windows High Contrast) the
   browser's own radio comes back, in the system's colours: author backgrounds
   are removed there, and the dot with them. */
input[type=radio]:not(.visually-hidden) {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1.5px solid var(--color-text-muted);
  border-radius: 50%;
  background: var(--color-bg);
  cursor: pointer;
  transition: border-color 0.2s ease;
}
input[type=radio]:not(.visually-hidden):checked {
  border-color: var(--color-accent-hover);
  background: radial-gradient(circle, var(--color-accent-hover) 0 4px, var(--color-bg) 4.5px);
}
input[type=radio]:not(.visually-hidden):disabled { opacity: 0.4; cursor: not-allowed; }
@media (forced-colors: active) {
  input[type=radio]:not(.visually-hidden) { appearance: auto; -webkit-appearance: radio; }
}

/* --- Radio groups --------------------------------------------------------- */
/* THERE WERE NO FIELDSET RULES AT ALL until the store had to ask a question with
   three answers. Without them the browser drew its own 1990s inset border, and
   `.field label` — written for the caption ABOVE an input — applied to each
   option instead, rendering the three answers in small grey semibold type that
   read as three field labels rather than as a choice.

   THE RESET IS ON `fieldset` ITSELF, NOT ON .choice. There are eight fieldsets
   in the client area and only two are the store's choice groups; styling just
   those would leave six drawing the default inset box a few hundred pixels from
   ones that do not. The grouping is then carried by the LEGEND, styled as the
   field caption it is, which is what style.css does — it has no inset boxes
   anywhere. */
fieldset { border: 0; padding: 0; margin: 0 0 18px; max-width: 30rem; }
legend {
  padding: 0; margin-bottom: 8px;
  font-size: 14px; font-weight: 500; color: var(--color-text-secondary);
}

/* THE WHOLE ROW IS THE TARGET, not the 13px circle. Wrapping the input in its
   label already made the text clickable; giving the row padding and a border
   makes it a hit area a finger can find, which is the difference between usable
   and technically-operable on a phone. */
.choice__option {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 0 8px; padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  font-weight: 400; font-size: 15px;
  color: var(--color-text-primary);
}
.choice__option:hover { border-color: var(--color-border-hover); background: var(--color-bg); }
/* The radio is the panel's one radio (above); here it is only placed: 2px down,
   level with the first line of 15px text at 1.5. */
.choice__option input[type=radio] { margin: 2px 0 0; }
.choice__label { display: block; }

/* THE CHOSEN ANSWER IS DRAWN AS edge.lt DRAWS ITS POPULAR CARD (D87e): the
   lime border, --color-bg and --shadow-md — the one "this one" state the main
   site has. It was a lime wash, which edge.lt draws on no card. The checked
   radio beside the text says it without colour. */
.choice__option:has(input:checked) {
  border-color: var(--color-accent);
  background: var(--color-bg);
  box-shadow: var(--shadow-md);
}
/* The focus ring belongs on the ROW, not on the 13px circle inside it. */
.choice__option:focus-within { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.choice__help { margin: -2px 0 0; padding: 0 2px; }

/* THE AUTH CODE RULE THAT USED TO BE HERE IS GONE, AND SO IS THE FIELD (D40).

   It hid the authorisation-code box unless the transfer radio in the same
   .product__form was checked, wrapped in @supports so a browser without :has()
   simply left it visible. It worked. What it could not do is stop the field
   EXISTING on a page where most visitors had no use for it, and it could not
   carry the typed name into it or price a term — CSS cannot read one input's
   value into another, which is the wall the whole two-form layout kept hitting.

   The order flow now asks for the code on a screen of its own, reached only by
   the visitors who chose to transfer, so there is nothing to conditionally hide.
   If a future form needs this trick again, the shape above is the one to copy:
   scope it to the form, and make the no-:has() fallback the VISIBLE state, never
   a field the client cannot reach. */

/* --- Register or sign in: the choice above both forms (Sec 10.2 step 2) -----
   TWO LINKS THAT LOOK LIKE ONE CONTROL. They sit on the registration page and
   the login page, in the same place on both, so that moving between them reads
   as switching a setting rather than as leaving the page.

   THE ROW BORROWS .choice__option's SHAPE ON PURPOSE — same padding, same
   radius, same border token — because it answers the same kind of question one
   screen earlier, and two differently-drawn pickers in one flow look like two
   unrelated things. It is NOT .choice__option: that class is a radio row inside
   a fieldset and its rules select on `:has(input:checked)`, which there is no
   input here to satisfy.

   THE CURRENT PAGE IS SELECTED THROUGH aria-current, NOT A MODIFIER CLASS.
   There is then exactly one attribute deciding both what a screen reader
   announces and what the eye sees, so the highlight cannot end up on a
   different option from the one that is open — the failure a second class
   invites the first time somebody edits the markup and not the other half.

   FONT-WEIGHT CARRIES IT AS WELL AS COLOUR. Colour is never the only signal
   (Sec 21), and this control's whole job is to say which of two things you are
   looking at. */
/* .type-choice IS THE SAME CONTROL ONE ROW DOWN. (D66) The registration page now
   carries two of these strips: how you get in, and then which kind of account
   you are opening. They are declared TOGETHER rather than copied, because the
   one thing that would be read as a fault is the two of them disagreeing about
   a radius or a weight while sitting six pixels apart. The type strip is
   narrower — two short words against two phrases — so only its flex-basis
   differs, below. */
.account-choice,
.type-choice {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 0 0 24px; max-width: 30rem;
}
.account-choice__option,
.type-choice__option {
  flex: 1 1 12rem; text-align: center;
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  font-size: 15px; font-weight: 400;
  text-decoration: none;
  color: var(--color-text-secondary);
}
.type-choice__option { flex: 1 1 8rem; }
.account-choice__option:hover,
.type-choice__option:hover { border-color: var(--color-border-hover); background: var(--color-bg); }
/* THE OPEN OPTION IS DRAWN AS edge.lt's POPULAR CARD (D87e) — lime border,
   --color-bg, --shadow-md — where it was a lime wash the main site uses on no
   card. The weight still says it without colour. */
.account-choice__option[aria-current="page"],
.type-choice__option[aria-current="page"] {
  border-color: var(--color-accent);
  background: var(--color-bg);
  box-shadow: var(--shadow-md);
  color: var(--color-text-primary);
  font-weight: 600;
}

/* --- Buttons -------------------------------------------------------------- */
/* THE SHAPE IS style.css's, NOT THIS FILE'S OWN. The panel drew its primary
   button as a PILL (--radius-pill, 999px) while edge.lt's .btn-primary — the
   button a visitor clicks to arrive here — is a 12px rounded rectangle. A
   customer crosses from one to the other mid-purchase, so the two were visibly
   different buttons for the same act.

   style.css IS THE SOURCE OF TRUTH for this, and the metrics below are its
   .btn-primary: 48px minimum height, 2rem of horizontal padding, 14px/500 type,
   a transparent 1px border so the box does not jump on hover, and the lift and
   press it already uses. --radius-md is 12px, the same number style.css writes
   literally; it is spelled as the token here because the panels keep their radii
   as tokens and the site has no use for them.

   PILL IS STILL RIGHT FOR BADGES further down — style.css uses 999px for its own
   soft and tag shapes. It was only ever wrong for the CTA. */
button, .button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px;
  padding: 0 2rem;
  font: inherit; font-size: 14px; font-weight: 500;
  color: var(--btn-primary-text);
  background: var(--btn-primary-bg);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  white-space: nowrap;
  transition: all 0.2s ease;
  cursor: pointer;
}
button:hover, .button:hover {
  background: var(--btn-primary-bg-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}
button:active, .button:active { transform: scale(0.98); }
/* A LINK DRAWN AS A BUTTON READS AS ONE: NO UNDERLINE, ANYWHERE (D103). This
   used to be scoped to .row-actions, so it held for the dashboard's service
   rows and nowhere else — every other <a class="button"> in the panel (the pay
   and PDF buttons on an invoice, the cart's checkout, the bank tiles, the page
   after a payment) drew a button with an underlined label, because `a` is
   underlined by default and nothing else took it off. */
a.button { text-decoration: none; }
button[disabled] { opacity: .4; cursor: not-allowed; }
button[disabled]:hover { transform: none; box-shadow: none; }

/* A BUTTON IN A TABLE CELL IS A ROW ACTION, NOT A CALL TO ACTION. style.css has
   a second button for exactly this — .btn-soft: 42px, pill, --color-surface, in
   secondary text at weight 400 — and using the 48px dark CTA for all five of the
   panel's row actions (two Remove in the cart, three in domains and dashboard)
   would put a heavy black block in every row of a table.
   SCOPED BY POSITION RATHER THAN BY CLASS so no view has to be edited to opt in,
   and so a row action added later cannot forget to. `td` IS the definition of a
   row action here; anything outside a table is still the CTA. */
td button, td .button {
  min-height: 42px;
  padding: 0 20px;
  font-size: 15px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--color-text-secondary);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
}
td button:hover, td .button:hover {
  border-color: var(--color-border-hover);
  background: var(--color-bg);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-sm);
}
/* THE SECOND CONTROL IN A SECTION THAT ALREADY HAS A CALL TO ACTION. (D66)
   The verification step asks for one thing — the code — and offers one way out
   if the mail never came. Drawn as the 48px dark CTA, the way out weighs exactly
   as much as the thing the client is meant to do, and two identical black
   buttons one above the other say neither is the answer.
   THE VOCABULARY IS td button's, NOT A NEW ONE: surface, secondary text, a real
   border. That pairing is already drawn in every table in the panel, so this
   introduces no colour the stylesheet has not already had to justify.
   IT KEEPS THE CTA's HEIGHT. A 48px target next to a 42px one reads as a
   mistake when they are both in the same short column, and 48px is the size a
   thumb is entitled to either way. */
.button--quiet {
  font-weight: 400;
  color: var(--color-text-secondary);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}
.button--quiet:hover {
  background: var(--color-bg);
  border-color: var(--color-border-hover);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-sm);
}

.inline-form { display: inline-flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* A TABLE ROW'S ACTIONS SIDE BY SIDE (D89): the services list offers a form
   (renew early) and links (the invoice, change plan) in one cell. They wrap
   onto a second line in a narrow cell rather than pushing the table wider. */
.row-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.row-actions form { margin: 0; }
/* THE WHOLE ROW OF BUTTONS AT THE RIGHT-HAND END, TOGETHER (D134) — the
   invoice list's "Mokėti dabar" and "Atsisiųsti PDF". D132 pushed the PDF
   alone to the end with .row-actions__end, and on a wide screen Pay was left a
   page-width away from it. Aligning the pair keeps them side by side, and a
   paid invoice's lone PDF still ends at the same edge as every other row's. */
.row-actions--right { justify-content: flex-end; }
/* A PANEL OPENED FROM A ROW'S BUTTONS (D103) — a domain's DNSSEC or
   nameservers — is drawn under the row of them. Without a gap its heading sat
   against the buttons as if it were part of the row. */
td .row-actions ~ details { margin-top: 16px; }


/* A TABLE WIDER THAN THE SCREEN SCROLLS INSIDE ITSELF (D89), so a phone does
   not scroll the whole page sideways. The table's own bottom margin moves to
   the wrapper, or the scrollbar would sit 24px below the last row. */
/* position: relative KEEPS THE SCREEN-READER TEXT INSIDE THE SCROLL (D132).
   .visually-hidden is position: absolute, so without a positioned ancestor its
   1px box is placed against the page, not this wrapper — and a row button's
   hidden words (the invoice list's "— Išankstinė sąskaita 12") stretched the
   page sideways on a phone exactly as the wide table used to. */
.table-scroll { position: relative; overflow-x: auto; margin: 0 0 24px; }
.table-scroll > table { margin: 0; }

/* --- Tables --------------------------------------------------------------- */
table { border-collapse: collapse; width: 100%; margin: 0 0 24px; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--color-border); vertical-align: top; }
thead th { font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-muted); }

/* An invoice line's period, under its name (D141): "2026-10-02 – 2027-10-01",
   the last day covered. Secondary, because the name is what the reader scans
   for; normal weight, because it sits inside the row's <th>. */
.line-period { display: block; margin-top: 2px; font-size: 14px; font-weight: 400; color: var(--color-text-secondary); }

/* --- The domain list, Mano domenai ------------------------------------------
   THE TERM DROPDOWN KEEPS ITS OWN WIDTH. Every select above is width:100%, and a
   percentage width in a table cell adds nothing to the column's minimum, so the
   column shrank under its dropdown: "1 metai — 14,00 €" was cut to "1 metai —"
   on a tablet and to "1" on a phone. */
.domains-table select { width: auto; max-width: 100%; }
/* THE TERM DROPDOWN'S OWN LABEL (D150), under the expiry date and drawn as a
   form field's caption is (.field label), with room above it so it does not
   read as part of the date. */
.domains-table .term-label {
  display: block; margin: 10px 0 4px;
  font-size: 14px; font-weight: 500; color: var(--color-text-secondary);
}
/* ITS SAVE, ON ITS OWN LINE RIGHT UNDER THE DROPDOWN (D158). It used to sit at
   the far end of the actions cell (.row-actions__end, gone), where it read as
   the transfer code's button. Stacked rather than side by side, so the column
   stays as narrow as the dropdown and the actions keep their room. */
.domains-table .term-form button { display: block; margin-top: 8px; }
/* THE REGISTRAR'S OWN DATA (D178), "Tiesioginiai duomenys iš registratoriaus",
   opened by "Registratoriaus duomenys": three labelled values in the panel's
   specification look (.product__spec — 14px at 1.6, the label muted, the value
   in the text colour), but ONE GRID FOR THE THREE ROWS, so the values start in
   one column. Drawn with .product__spec-row each row was a grid of its own and
   sized its label to itself, and the three values began at three different
   places. The same rule, in the admin's size, is in admin.css. */
.registry-info {
  display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px 12px;
  margin: 8px 0 0; font-size: 14px; line-height: 1.6;
}
.registry-info dt { color: var(--color-text-muted); }
.registry-info dd { margin: 0; color: var(--color-text-primary); overflow-wrap: anywhere; }

/* ON A PHONE, ONE CARD PER DOMAIN. Four columns — a name, a status, a date with
   its dropdown, and a row of buttons — do not fit 390px; the table ran off the
   side and the whole page scrolled sideways, with the buttons out of sight. Here
   the name heads each card, the status and the date take their column name from
   data-label, the term's Save under its dropdown, and the row's other buttons
   underneath. The header row is hidden only visually, as
   .visually-hidden does it, so it is still there for a screen reader. */
@media (max-width: 639px) {
  .domains-table thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
  .domains-table, .domains-table tbody, .domains-table tr,
  .domains-table th, .domains-table td { display: block; }
  .domains-table tr { padding: 14px 0; border-bottom: 1px solid var(--color-border); }
  .domains-table tbody th, .domains-table td { padding: 4px 0; border-bottom: 0; }
  .domains-table tbody th { font-size: 17px; overflow-wrap: anywhere; }
  .domains-table td[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 2px;
    font-size: 13px; text-transform: uppercase; letter-spacing: .04em;
    color: var(--color-text-muted);
  }
  .domains-table .row-actions { margin-top: 8px; }
}

.badge {
  display: inline-block; font-size: 12px; font-weight: 500;
  padding: 2px 10px; border-radius: var(--radius-pill);
  background: var(--color-surface); color: var(--color-text-secondary);
  border: 1px solid var(--color-border);
}

section { margin: 0 0 40px; }

/* --- Cards: plans in the store, additional logins on the users page --------- */
/* THE SIGNATURE SHAPE. style.css draws .pricing-card as
   `border-radius: 0 20px 20px 20px` — square top-left, round everywhere else —
   and it is the most recognisable thing on the marketing site. A store plan IS
   the panel's pricing card, so it takes the same corner rather than the generic
   14px box it had.

   THE BACKGROUND STAYS WHITE, and that is a real divergence with a reason. The
   site puts a --color-surface (#fafafa) card on a white page; the panel puts
   cards on --color-page (#f5f5f7), where #fafafa would all but disappear. Same
   intent — the card sits forward of its page — reached with the two colours
   swapped because the pages underneath are not the same colour.

   NO HOVER LIFT, unlike the site's. .pricing-card is a LINK, and lifting under
   the cursor tells you so. This card is a FORM: the pointer is inside it to use
   a select or type a domain, and a card that rises when you reach for a field
   is noise rather than signal. */
.product {
  border: 1px solid var(--color-border);
  border-radius: 0 20px 20px 20px;
  background: var(--color-surface-elevated);
  padding: 20px 24px;
  margin: 0 0 20px;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.product h2 { margin-top: 0; }

/* --- What a plan includes: store, cart and every upgrade option ------------
   ONE RULESET FOR ALL THREE, because the answer to "what do I get" should not
   look like a different kind of fact depending on which screen asks it. The
   text is operator-typed and escaped line by line before it reaches here. */
.product__spec {
  margin: 0 0 16px;
  padding: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* THE LABELLED FORM, when a plan's specification is in the four-line shape.
   Same size, same rhythm and the same muted grey as the unlabelled form above,
   because a plan that names its rows and one that does not must still read as
   the same kind of fact on the same page.

   THE LABEL RECEDES AND THE VALUE DOES NOT. Every plan in the store carries the
   identical four labels, so they are signposts and nothing more; what differs
   between one plan and the next is the number, and that is the only thing here
   given primary text. Setting both the same weight would make a column of
   repeated words compete with the four figures a buyer is comparing.

   A GRID, NOT A TABLE OR A FLOAT. max-content sizes the label column to the
   longest label — "El. pašto dėžutės" in Lithuanian, "Email mailboxes" in
   English — so the values line up in one column without a width being guessed
   here and then found wrong in the other language. */
.product__spec--labelled { display: grid; gap: 2px 0; }
.product__spec-row {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0 12px;
}
.product__spec-row dt { color: var(--color-text-muted); }
.product__spec-row dd {
  margin: 0;
  color: var(--color-text-primary);
}

/* AN UPGRADE CHOICE IS A BLOCK, NOT A LINE IN A DROPDOWN. It carries a label
   and a specification list, so it needs room and a boundary between options —
   an <option> could hold neither, which is why this replaced a <select>.

   THE SAME BUG .choice__option WAS WRITTEN TO FIX WAS STILL LIVE HERE. These
   options sit inside <fieldset class="field">, so `.field label` — the rule for
   the caption ABOVE an input — was styling each ANSWER: 14px, weight 500,
   secondary grey, and display:block, which put the radio on one line and its
   own label on the next. Three plan choices rendered as three field captions
   with orphaned radio buttons above them. The store's choice groups were
   corrected and this one was not, because the two are different markup and
   nothing renders them side by side.

   A GRID RATHER THAN .choice__option's FLEX, because the markup is different:
   there the input is INSIDE the label, here the input, the label and the
   spec list are three siblings. Two columns put the radio in the first and
   everything that describes it in the second, which is the layout the reader
   expects and the one .choice__option produces by nesting. */
.upgrade-option {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 10px;
  align-items: start;
  padding: 12px 0;
  border-top: 1px solid var(--color-border);
}
.upgrade-option:first-of-type { border-top: 0; }
.upgrade-option input[type=radio] { margin: 2px 0 0; }
/* THE ANSWER'S OWN TYPE, matching .choice__option exactly: 15px at weight 400
   in primary text, which is the site's .pricing-feature step. It is a thing to
   read and choose, not a caption. */
.upgrade-option label {
  display: block;
  margin: 0;
  font-size: 15px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--color-text-primary);
}
.upgrade-option .product__spec { grid-column: 2; margin: 6px 0 0; }

/* A capability the owner may not delegate. The checkbox is disabled and the row
   says "never copied"; this only stops it reading as an available choice. */
label.disabled { opacity: .55; cursor: not-allowed; }

/* A CHECKLIST: ONE BOX PER LINE, THE BOX FIRST (D104). The Users page drew its
   permissions and mail copies as bare inline labels, so the list flowed like a
   sentence and every line break left the NEXT item's box at the end of the line
   before — the checkboxes seemed to sit at the end of each row, belonging to
   the wrong words. Each label is now a row of two: the box, then its text in a
   <span>, top-aligned so a label that wraps keeps its box beside the first
   line and its second line under the first rather than under the box.

   COLUMNS WHERE THERE IS ROOM. Some thirty mail types in one column is a long
   scroll; auto-fill puts two or three side by side on a wide card and one on a
   phone, and every row still begins with its own box.

   THE FIELDSET GIVES UP ITS 30rem HERE. That width is for a column of typed
   fields; a checklist beside the permissions above it should be as wide as
   they are, not half of it. */
.check-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
  gap: 8px 24px;
  margin: 0 0 18px;
}
.check-row {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0;
  font-size: 15px; font-weight: 400; line-height: 1.4;
  color: var(--color-text-primary);
  cursor: pointer;
}
/* 3px down: level with the middle of the first 15px line at 1.4. */
.check-row input[type=checkbox] { flex: 0 0 auto; margin: 3px 0 0; }
fieldset:has(> .check-list) { max-width: none; }

/* THE USERS PAGE'S QUICK CHOICES (D133): "Pažymėti visus" / "Nužymėti" and
   "Aktualūs pridedant buhalterį", in a row of their own at the RIGHT-HAND END,
   above the boxes they set — under the address on the invitation, at the top of
   each login's form. Quiet buttons: they tick boxes and save nothing, so they
   must not look like the form's own button. On a phone they wrap and stay right.
   The note under them says that nothing is saved yet, where the owner is
   looking. */
.check-tools {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  margin: 0 0 14px;
}
.check-tools__note { text-align: right; margin-top: -6px; }

/* --- Tickets: ours and theirs in the same shape, deliberately --------------- */
.reply {
  border: 1px solid var(--color-border);
  border-left-width: 3px;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 14px 18px;
  margin: 0 0 16px;
}
.reply p:last-child { margin-bottom: 0; }

.attachments { list-style: none; margin: 12px 0 0; padding: 0; }
.attachments li { padding: 2px 0; font-size: 14px; }
.attachments a { color: var(--color-text-secondary); text-decoration: underline; }
.attachments a:hover { color: var(--color-text-primary); }

/* --- A stored email, as the plain text it was sent as ------------------------
   PRE, SO IT MUST BE TOLD TO WRAP, or a long invoice line scrolls the page
   sideways on the phone where most of these are read. */
.email-body {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 16px 18px;
  margin: 0 0 24px;
}

/* --- The working counts, on the client dashboard -----------------------------
   DELIBERATELY IDENTICAL TO admin.css, the same way .reply and .banner are. An
   operator looking at their own dashboard and a customer looking at theirs are
   reading the same component, and it should not be a different component
   because it is a different hostname.

   FOUR TILES HERE AND SEVEN THERE. The client's are the four things they came
   to look up — what they run, what they own, what they owe, what they asked —
   and they replace those entries in the top navigation rather than duplicating
   them.

   FILLED CHARCOAL, IN BOTH PALETTES. --card-bg is pinned rather than derived
   from --btn-primary-bg for the reason given beside the tokens: the CTA pair
   inverts and this must not.

   THE LABEL IS NOT FADED. --color-text-muted on charcoal is 3.32:1 and fails AA;
   both lines are full-strength white and the hierarchy comes from size and
   weight. --card-text-dim is spent only on a zero, which at 28px is large text
   and needs 3:1 rather than 4.5:1. */
/* THE DASHBOARD'S GREETING IS ITS h1 (D102) — "Labas rytas, Donatai" where
   "Apžvalga" used to be. Same size and weight as every other page's h1, so the
   panel keeps one heading style; only the space under it changes. The bare h1
   leaves 8px because on every other page a paragraph follows it, and here the
   dark count tiles come straight after — 8px put the welcome and the numbers
   in one cramped block. 24px is the gap the tiles themselves leave below. */
.greeting { margin: 0 0 24px; overflow-wrap: anywhere; }

.cards {
  list-style: none;
  padding: 0;
  margin: 0 0 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.card {
  flex: 1 1 150px;
  border: 1px solid transparent;
  /* THE SIGNATURE SHAPE — the same `0 20px 20px 20px` .product carries a few
     rules down, and for the same reason: style.css draws .pricing-card and the
     /domenai price cards with a square top-left corner, and that corner is the
     branding. A tile that is charcoal but generically rounded is still not our
     card. The anchor repeats it so the hover fill does not square off three
     corners. */
  border-radius: 0 20px 20px 20px;
  padding: 12px 16px;
  background: var(--card-bg);
  color: var(--card-text);
  transition: all 0.2s ease;
}
.card a {
  display: block;
  text-decoration: none;
  color: inherit;
  margin: -12px -16px;
  padding: 12px 16px;
  border-radius: 0 20px 20px 20px;
}
.card a:hover { background: var(--card-bg-hover); }
.card:has(a):hover { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.card:has(a):active { transform: scale(0.995); }
.card__count {
  display: block;
  font-size: 28px;
  font-weight: 600;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.card__label { display: block; font-size: 13px; }
.card--zero .card__count { color: var(--card-text-dim); font-weight: 500; }

/* IN DARK MODE THE FILL CANNOT DRAW THE TILE. #1d1d1f on the #0b0b0c page is
   1.50:1 — pinned charcoal on a near-black page is very nearly the page. The
   border does the work instead, at the hover weight because --color-border is
   itself only 1.16:1 against the fill. */
@media (prefers-color-scheme: dark) {
  .card { border-color: var(--color-border-hover); }
}

/* --- Set-apart sections: delete my account, turn auto-renew off --------------
   NOT COLOURED RED: the confirmation step does the protecting, and a block that
   shouts on every visit stops being read before the day it matters. */
.danger { margin-top: 32px; padding-top: 16px; border-top: 1px solid var(--color-border); }
.danger h2 { margin-top: 0; }

/* --- Gateway bank list ------------------------------------------------------
   Rendered by a payment module, not by a view, so the classes live here rather
   than beside the markup.

   THIS BLOCK USED TO SAY "TEXT ONLY, NO LOGOS", on the grounds that img-src was
   'self' data: and every logo would render broken. THAT IS NO LONGER TRUE and
   had not been for some time: the CSP carries img-src https://*.paysera.com,
   the module emits <img class="paysera-method__logo">, and a test checks the
   allowlist against the module manifests in both directions. The note was
   describing a decision that had already been reversed, sitting directly above
   the rule that styles the logos it said could not exist — so anyone trusting
   it would have deleted working code. (D50)

   The links are <a>, never a form: form-action 'self' is enforced ACROSS
   REDIRECTS, so a form submission bound for bank.paysera.com is cancelled
   outright and silently, and the customer is left with a pay button that does
   nothing. Ordinary navigation is governed by no CSP directive. */
.paysera-group { margin: 0 0 20px; }
.paysera-group__title {
  font-size: 13px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-text-muted); margin: 0 0 10px;
}

/* A GRID, NOT A WRAPPING ROW. Free-flowing inline tiles sized themselves to
   their bank's name, so the list landed as ragged rows — seven across, then two
   — with nothing lining up. Equal columns let a customer scan down as well as
   across, and they fill the width the container allows instead of trailing off
   mid-row.

   auto-fill, NOT auto-fit: with few banks, auto-fit stretches the survivors to
   fill the row, so an operator whose feed returns three would get three tiles a
   third of the page wide each.

   THE COLUMN IS SIZED FROM THE LOGO, NOT FROM A GUESS (D102). It was 14rem —
   224px for a 166px chip — so every tile carried some 60px of empty surface
   beside its logo. 11.5rem is the chip plus the tile's padding plus a little
   air. The min() lets a phone keep two columns: at 390px one column of 11.5rem
   would not fit twice, and a single column there is a full-width tile around a
   150px logo, the same emptiness on its side. Below 11.5rem the chip and the
   logo shrink with the column (max-width on both), keeping the logo's shape. */
.paysera-methods {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(11.5rem, calc(50% - 4px)), 1fr));
  gap: 8px;
}

/* THE LIST ITEM IS A FLEX CONTAINER SO THE ANCHOR CAN FILL IT. A grid item
   stretches to its row's height; the <a> INSIDE it does not, and without this
   the anchor is only as tall as its own content. The bank name then sits
   wherever the logo leaves it rather than on the tile's bottom edge, and in a
   row where one name wraps to two lines and the others do not, the single-line
   names float at three different heights. */
.paysera-methods > li { display: flex; }

/* Set apart from the primary pay control: this is a shortcut past Paysera's own
   selector, not a second, competing way to pay.
   ── STACKED, LOGO ABOVE NAME, BOTH CENTRED ──────────────────────────────────
   This was a horizontal row: a small logo, then the name beside it, everything
   left-aligned. The note here used to argue AGAINST centring — that centred
   tiles put every logo at a different x and leave the eye nothing to run down.
   That was true of the row, where the logo's x depended on nothing and its
   neighbours' names were all different lengths. It is not true of a column:
   every logo is centred in a column of equal width, so they line up down the
   page more exactly than left alignment ever made them. The old reasoning is
   recorded here because it is the first thing anybody will think of undoing. */
.paysera-method {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  width: 100%; text-align: center;
  justify-content: flex-start;
  background: var(--color-surface);
  color: var(--color-text-primary);
  border: 1px solid var(--color-border);
  font-weight: 500; font-size: 14px; line-height: 1.3; padding: 10px;
}
/* min-width:0 lets a long name's tile shrink to its grid column instead of being
   pushed wider by its content. It does NOT make the name wrap — see the
   white-space note on the rule below, which is the part that does. */
.paysera-method > span { min-width: 0; }

/* :last-child, AND THE SELECTOR IS THE WHOLE POINT. The tile holds TWO spans —
   the chip around the logo and the name — and they are told apart only by
   position, because the name carries no class of its own. Written as
   `.paysera-method > span` this rule hit both: width:100% stretched the white
   chip across the full tile instead of leaving it an 88px square, and
   margin-top:auto on the FIRST span ate the free space before the name could
   claim any, so the name stopped sitting on the bottom edge at all. Both
   symptoms are visual only; nothing errors, and the markup keeps looking
   correct while the tile does neither of the things it was changed to do.

   white-space: normal IS LOAD-BEARING AND IT IS NOT DECORATION. The tile is an
   <a class="button paysera-method">, and `button, .button` sets
   white-space: nowrap. It is an INHERITED property, so the name span picked it
   up and COULD NOT WRAP AT ALL — a long enough bank title ran straight out of
   its tile and off the side of the page. min-width:0 and width:100% do not
   help with that and never did: the first lets a flex item shrink below its
   content, the second gives the span the tile's width, and neither has any
   effect on text that has been told not to break. The note above this rule used
   to claim they handled it. They did not.

   IT SURVIVED BECAUSE THE REAL DATA IS SHORT ENOUGH. The longest title Paysera
   currently returns for LT is "Lietuvos centrinė kredito unija", which fits at
   360px with room to spare, so nothing has ever been seen to spill. But that
   list is third-party XML that changes without notice, and every other line in
   this module treats it as exactly that.

   overflow-wrap: anywhere IS FOR THE TITLE WITH NO SPACES IN IT. white-space
   alone breaks at word boundaries, and a single long unbroken token has none. */
.paysera-method > span:last-child {
  width: 100%; margin-top: auto;
  white-space: normal; overflow-wrap: anywhere;
}

/* --- THE CHIP BEHIND THE LOGO, WHICH IS THE POINT OF THIS BLOCK -------------
   BANK LOGOS ARE DRAWN FOR WHITE BACKGROUNDS. Paysera serves them as-is, and
   several Lithuanian marks are dark navy or near-black with transparency around
   them. On the dark theme those render as a smudge on a near-black page, and on
   a high-contrast theme they effectively vanish — which is how a page WITH
   logos gets reported as a page without any.

   SO THE PANEL SUPPLIES THE BACKGROUND THE ARTWORK ASSUMES rather than hoping
   each mark survives whatever is behind it. Pinned white in every theme, for
   the same reason the count tiles pin their charcoal: a chip built from
   --color-surface would invert in dark mode and put the logos straight back on
   a dark field.

   THE INSET RING, NOT A BORDER: on the light theme a white chip on a white
   surface would otherwise have no edge at all.

   THE CHIP AND THE LOGO ARE THE SHAPE PAYSERA'S ARTWORK IS: 150 x 38 (D102).
   Every logo bank.paysera.com serves under payment_types/ is a 150 x 38 PNG,
   roughly 4:1, with the mark already centred in it. This used to force them
   into an 80 x 80 square (inside an 88 x 88 chip) with object-fit: contain,
   which drew each logo at 80 x 20 and filled the other 60px of the square with
   white, above and below. The earlier "make the logos bigger" change doubled
   that square from 40 to 80 — and with a 4:1 image, a square twice the size is
   mostly twice the empty band: the mark grew 40px wide while the tile grew
   88px tall. That was the huge padding around every logo.

   NOW THE LOGO IS DRAWN AT ITS OWN SIZE, 1:1, the sharpest it can be, and the
   chip is the logo plus 4px of white top and bottom and 8px either side — the
   margin that keeps a mark with no built-in padding off the chip's edge. On a
   column narrower than the chip, both shrink with it (max-width) and
   object-fit keeps the logo's shape rather than squashing it. */
.paysera-method__chip {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  box-sizing: border-box;
  width: 166px; max-width: 100%;
  padding: 4px 8px;
  background: #ffffff;
  border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
/* A bank whose logo Paysera did not vouch for still gets its column width, so
   one untrusted mark does not knock the grid out of alignment. It gets no chip
   though: an empty white square reads as a broken image, which is the thing the
   chip exists to avoid. It keeps the chip's height, so its name sits level with
   its neighbours'. */
.paysera-method__chip--empty { background: none; box-shadow: none; min-height: 46px; }

/* A logo that 404s must not leave a broken-image glyph beside the bank name:
   the name is the thing the customer actually needs. */
/* KEEP THESE TWO NUMBERS EQUAL TO THE width/height ATTRIBUTES PaySeraGateway
   EMITS. The attributes reserve the box before the image loads; a mismatch
   makes the grid settle at one height and then jump to another. */
.paysera-method__logo { width: 150px; height: 38px; max-width: 100%; object-fit: contain; display: block; }

/* --- The impersonation banner (Sec 14c) -------------------------------------
   THE ONLY BANNER IN THE CLIENT AREA, and it exists to stop an admin forgetting
   whose account they are driving — the moment they forget is the moment they
   act. So it sits above the header, on every page, and it is not dismissible.

   THE CLASS NAMES MATCH admin.css DELIBERATELY. The two stylesheets are
   separate on purpose and must not import from each other, but a warn banner
   should not look like a different component depending on which panel an admin
   is standing in. Same tokens, same shape, written out rather than shared.

   COLOUR IS NOT THE SIGNAL. The bold title carries the meaning for anyone who
   cannot distinguish the tint, and role="status" in the markup carries it for
   anyone who cannot see it at all. */
.banner {
  border: 1px solid var(--color-border);
  border-left-width: 4px;
  border-radius: var(--radius-md);
  padding: 14px 18px;
  margin: 0 0 24px;
  background: var(--color-surface);
}
.banner strong { display: block; margin-bottom: 4px; }
.banner a { text-decoration: underline; }
.banner--warn { border-left-color: var(--tint-orange-600); background: var(--tint-orange-50); }
/* The way out sits inside the banner, so it is never further away than the
   reminder that you need it. */
.banner .inline-form { display: inline; margin-left: 8px; }

/* --- Domain availability, on the order flow's second step (D41) -------------
   THE THREE STATES ARE edge.lt's THREE STATES. domenai.html's search cards say
   "laisvas", "užimta" and "nepavyko patikrinti", and a visitor who searched
   there and clicked through meets the same three answers here rather than a
   second vocabulary for the same facts.

   THE TINTS ARE THE PANEL'S DECLARED STATUS TINTS, not the marketing site's.
   The site greys a taken card because taken there only disables a link; taken
   HERE refuses the order, so it is a failure state and reads as one.

   COLOUR IS NEVER THE CARRIER. Each of these renders a full sentence — the tint
   is reinforcement, and the page is equally readable in monochrome, which is
   this stylesheet's standing rule rather than a preference. */
.availability {
  margin: 0 0 16px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: 15px;
  line-height: 1.45;
}
.availability--free {
  background: var(--tint-green-50);
  color: var(--tint-green-600);
}
.availability--taken {
  background: var(--tint-red-50);
  color: var(--tint-red-600);
}
.availability--unknown {
  background: var(--tint-orange-50);
  color: var(--tint-orange-600);
}

/* =============================================================================
   THE ORDER FORM (D87) — the plans, the chosen plan's terms, and one domain,
   as ONE form. It replaced a form per plan: six copies of the same fields with
   the carried name in every one. The client area runs no script (D32), so
   everything that responds to a choice below is a CSS selector on a checked
   radio.

   EVERY CHOICE IS A HIDDEN RADIO FOLLOWED BY ITS VISIBLE LABEL. The radio stays
   in the page (.visually-hidden, never display:none) so it keeps its keyboard
   behaviour and its place in the accessibility tree; the label beside it is the
   card that is drawn, clicked and ringed. "+" styles the label of the checked
   radio and "~" reveals what belongs to it — neither needs :has(), and neither
   needs a rule per plan, so a plan added in the admin panel is drawn by these
   rules as it stands.

   THE CHOSEN ANSWER IS NOT COLOUR ALONE (WCAG 1.4.1). Each option carries a
   radio dot that fills when chosen, as well as the lime border.
   ============================================================================= */

/* --- Where the visitor is: plan and domain, review, cart ------------------ */
.order-progress {
  list-style: none;
  margin: 4px 0 32px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  font-size: 14px;
}
.order-progress__step { display: flex; align-items: center; gap: 8px; color: var(--color-text-muted); }
.order-progress__step + .order-progress__step::before {
  content: "";
  width: 24px;
  height: 1px;
  margin-right: 4px;
  background: var(--color-border-hover);
}
.order-progress__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--color-border-hover);
  background: var(--color-surface-elevated);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
}
/* THE ATTRIBUTE, NOT A CLASS, marks the current step — what a screen reader
   announces and what the eye sees are the same fact. */
.order-progress__step[aria-current="step"] { color: var(--color-text-primary); font-weight: 500; }
.order-progress__step[aria-current="step"] .order-progress__num {
  border-color: var(--btn-primary-bg);
  background: var(--btn-primary-bg);
  color: var(--btn-primary-text);
}
/* A FINISHED STEP IS edge.lt's LIME-TINTED CIRCLE (.feature-tile-icon): the soft
   accent fill and no ring (D87e). The number inside stays in the text colour. */
.order-progress__step--done .order-progress__num {
  border-color: transparent;
  background: var(--color-accent-soft);
  color: var(--color-text-primary);
}
.order-progress__label { color: inherit; }
a.order-progress__label { text-decoration: underline; text-underline-offset: 3px; }

/* --- After paying: the order, step by step (D100) ------------------------
   THE PAYMENT PAGE'S PROGRESS LIST. One row per step: a bare 22px icon and two
   lines of text. NO CIRCLE AND NO BACKGROUND behind the icon, deliberately —
   the checkout stepper above draws circles, and this page is meant to read
   quieter than a form. Every state is also written in words; the icons are
   aria-hidden decoration.
   NO NEW COLOURS: the lime is --color-accent (the lights) and
   --color-accent-hover (the check, darker for contrast on white); everything
   else is the text scale. SET AS fill, NEVER color: these are shapes, not
   text, and the contrast audit rightly reads any `color:` as text. Both follow dark mode through the tokens. */
.progress-steps { list-style: none; margin: 8px 0 28px; padding: 0; }
.progress-steps__item { display: flex; align-items: flex-start; gap: 14px; padding: 10px 0; }
.progress-steps__icon { flex: 0 0 22px; width: 22px; height: 22px; margin-top: 1px; }
.progress-steps__icon svg { display: block; }
.progress-steps__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.progress-steps__name { color: var(--color-text-primary); overflow-wrap: anywhere; }
.progress-steps__state { font-size: 15px; color: var(--color-text-secondary); }
.progress-steps__item--open .progress-steps__name { color: var(--color-text-secondary); }
.progress-steps__item--active .progress-steps__name { color: var(--color-text-primary); font-weight: 500; }
.progress-steps__item--done .progress-steps__name,
.progress-steps__item--note .progress-steps__name { color: var(--color-text-primary); }
.progress-steps__check { fill: var(--color-accent-hover); }
.progress-steps__mark { fill: var(--color-text-muted); }
.progress-note { font-size: 15px; color: var(--color-text-secondary); margin: 0 0 20px; }

/* THE LIGHTS. A 4x4 matrix; each dot is given, per page render, one of two
   blink shapes (k), five speeds (s) and eight starting points (o) — the page
   reloads every ten seconds and a fixed animation would visibly restart, so
   the randomness comes from the server and every reload starts somewhere new.
   NEGATIVE DELAYS start each dot mid-cycle, so there is no frame where they
   all begin together. Still and dim when the step is not the one moving. */
.led-matrix__dot { fill: var(--color-text-muted); opacity: .16; }
.led-matrix--idle .led-matrix__dot { opacity: .2; }
.led-matrix--live .led-matrix__dot { fill: var(--color-accent); opacity: .3; animation: ledBlink 2.3s linear infinite; }
.led-matrix--live .led-k1 { animation-name: ledFlicker; }
.led-s0 { animation-duration: 1.3s; }  .led-s1 { animation-duration: 1.9s; }
.led-s2 { animation-duration: 2.3s; }  .led-s3 { animation-duration: 2.9s; }
.led-s4 { animation-duration: 3.7s; }
.led-o0 { animation-delay: -.1s; }  .led-o1 { animation-delay: -.5s; }
.led-o2 { animation-delay: -.9s; }  .led-o3 { animation-delay: -1.3s; }
.led-o4 { animation-delay: -1.8s; } .led-o5 { animation-delay: -2.2s; }
.led-o6 { animation-delay: -2.7s; } .led-o7 { animation-delay: -3.1s; }
@keyframes ledBlink   { 0%, 100% { opacity: .3; } 6% { opacity: 1; } 30% { opacity: .85; } 46% { opacity: .3; } }
@keyframes ledFlicker { 0%, 100% { opacity: .3; } 30% { opacity: .3; } 36% { opacity: 1; } 44% { opacity: .5; } 50% { opacity: 1; } 72% { opacity: .8; } 84% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) {
  .led-matrix--live .led-matrix__dot { animation: none; opacity: .7; }
}

/* --- The form and its sections -------------------------------------------- */
.order { margin: 0; }
.order__section { margin: 0 0 40px; }
.order__heading {
  margin: 0 0 14px;
  padding: 0;
  font-size: 19px;
  line-height: 1.26;
  font-weight: 600;
  letter-spacing: .006em;
  color: var(--color-text-primary);
}
/* THE GLOBAL fieldset IS A NARROW FORM COLUMN (30rem). These three are page
   width: a row of plans, a row of terms, a row of three answers. */
.plan-picker, .plan-option__terms, .domain-choice { max-width: none; min-width: 0; margin: 0; }

/* --- The plans: a grid of cards --------------------------------------------
   TWO ACROSS ON A PHONE, THREE ON A TABLET, ONE ROW ON A DESKTOP.

   THE DESKTOP ROW IS FLEX, NOT AN auto-fit GRID, and the grid was tried first.
   auto-fit only collapses an EMPTY track, and the terms row spans every track
   (1 / -1), so no track is ever empty: six plans left a seventh column standing
   blank at the end of the row. Flex needs no column count at all — every card
   takes an equal share of the row, so four plans make four wide cards — and
   the terms, a whole line wide and ordered after the cards, wrap onto a line
   of their own. */
.plan-picker__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
@media (min-width: 640px) {
  .plan-picker__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .plan-picker__grid { display: flex; flex-wrap: wrap; }
  .plan-option__card { flex: 1 1 0; min-width: 8.5rem; }
  .plan-option__terms, .plan-picker__placeholder { flex: 0 0 100%; }
}
/* THE WRAPPER DRAWS NOTHING, so its radio, card and terms are grid items of
   the grid above — which is what lets the chosen plan's terms take a row of
   their own under every card instead of pushing the next card aside. */
.plan-option { display: contents; }

/* THE SIGNATURE SHAPE — square top-left, 20px elsewhere — as .product and
   .card draw it, so a plan here is recognisably the plan on edge.lt. */
.plan-option__card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px 16px 16px;
  border: 1px solid var(--color-border);
  border-radius: 0 20px 20px 20px;
  background: var(--color-surface-elevated);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: border-color 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease;
}
/* edge.lt's NEUTRAL CARD HOVER — .pricing-card.domain-card:hover and
   .is-feature-tile:hover: the border one step darker and the card brought to
   --color-bg. Not the plan card's lime hover: on edge.lt that card is a link,
   and here lime means CHOSEN, so a lime hover would look like a choice. */
.plan-option__card:hover { border-color: var(--color-border-hover); background: var(--color-bg); }
.plan-option__name { padding-right: 26px; font-size: 17px; line-height: 1.25; font-weight: 600; color: var(--color-text-primary); }
.plan-option__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 4px; }
.plan-option__from, .plan-option__period { font-size: 13px; color: var(--color-text-secondary); }
.plan-option__amount {
  font-size: 24px;
  line-height: 1.1;
  font-weight: 600;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-primary);
}
/* THE PANEL'S OWN SPECIFICATION STYLE (D87c) — .product__spec, as the cart and
   the upgrade screens draw the same four lines: 14px at 1.6, the label muted,
   the value in the text colour at normal weight, 2px between rows, and NO
   DIVIDERS; edge.lt's cards have none either. D87 had drawn them smaller, with a
   rule under the price and 8px between rows, which read as a different thing.
   On a card this narrow each label sits over its value. */
.plan-option__spec { display: grid; gap: 2px; font-size: 14px; line-height: 1.6; }
.plan-option__spec-row { display: flex; flex-direction: column; }
.plan-option__spec-label { color: var(--color-text-muted); }
.plan-option__spec-value { color: var(--color-text-primary); }

/* --- "Hostingo nereikia": a line, not a card (D123) -------------------------
   THE OPERATOR'S DESIGN. D121 drew it as a first card; it is now the words
   alone — the plan name's size and weight, on one line — with the chosen badge
   at their end. On a desktop it sits in the empty top of the label column,
   level with the plan names (the table rules below place it); everywhere else
   it is a line of its own above the cards.

   NO OUTLINE, SURFACE OR SHADOW, and no empty box while another plan is
   chosen — the same rule as the cards' badge (D105): the badge appears on the
   one answer chosen and nowhere else. Unchosen, the words are the secondary
   colour and darken on hover, which is what says they can be clicked. */
.plan-picker__none {
  grid-column: 1 / -1;
  padding: 4px 0 0;
  cursor: pointer;
}
.plan-picker__none-text {
  position: relative;
  display: inline-block;
  padding-right: 30px;
  font-size: 17px;
  line-height: 1.25;
  font-weight: 600;
  white-space: nowrap;
  color: var(--color-text-secondary);
  transition: color 0.3s ease;
}
.plan-picker__none:hover .plan-picker__none-text,
.plan-option__input--none:checked + .plan-picker__none .plan-picker__none-text { color: var(--color-text-primary); }
@media (min-width: 1024px) {
  .plan-picker__none { flex: 0 0 100%; }
}

/* --- The chosen plan's terms, in a row of their own ----------------------- */
/* MARGIN, NOT PADDING: a fieldset draws its legend above its padding, so padding
   would have put the space under the heading instead of above it. */
.plan-option__terms { display: none; order: 1; grid-column: 1 / -1; margin: 22px 0 0; }
.plan-option__input:checked ~ .plan-option__terms { display: block; }
/* UNTIL A PLAN IS CHOSEN the row says where the terms will be. :has() here is
   decoration only: without it the note simply never shows. */
.plan-picker__placeholder {
  display: none;
  order: 1;
  grid-column: 1 / -1;
  margin: 22px 0 0;
  padding: 16px 18px;
  border: 1px dashed var(--color-border-hover);
  border-radius: var(--radius-lg);
  font-size: 14px;
  color: var(--color-text-muted);
}
.plan-picker__grid:not(:has(.plan-option__input:checked)) .plan-picker__placeholder { display: block; }

.term-picker { display: flex; flex-wrap: wrap; gap: 10px; }
.term-option {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 9rem;
  padding: 12px 40px 12px 18px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface-elevated);
  cursor: pointer;
  transition: border-color 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease;
}
.term-option:hover { border-color: var(--color-border-hover); background: var(--color-bg); }
.term-option__length { font-size: 16px; font-weight: 600; color: var(--color-text-primary); }
.term-option__price { font-size: 14px; font-variant-numeric: tabular-nums; color: var(--color-text-secondary); }
/* A THIRD LINE ON A TERM (D89) — the plan change says what choosing it does
   to the remaining time. Smaller than the price, in the same colour, so the
   length and the rate still lead. */
.term-option__note { font-size: 13px; line-height: 1.4; color: var(--color-text-secondary); }

/* --- One domain: three answers, then the name ----------------------------- */
.domain-choice__options { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
@media (min-width: 720px) {
  .domain-choice__options { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* "HOSTINGO NEREIKIA" TAKES "I ALREADY OWN IT" AWAY (D121, Amendment 33). With
   no hosting account there is nothing for an existing domain to be the name
   of, so while that card is chosen the answer is not offered, its explanation
   does not show, and the two answers left share the row between them.

   THE RADIO GOES TOO, NOT ONLY ITS CARD (D122). It is visually hidden like
   every radio here, which keeps it in the tab order and the accessibility
   tree: with only the card gone, the arrow key from "Registruoti" chose an
   answer nobody could see, and a screen reader still read it out. display:none
   takes it out of both. A radio hidden this way that is already chosen is
   still sent with the form, so the store's refusal below still meets it.

   :has() IS DECORATION HERE, NOT THE RULE. The store refuses "own" without a
   plan, on the domain question, in the page's language — which is what a
   browser without :has() meets, and a visitor who chose "own" before switching
   to no hosting (a hidden radio stays chosen). The client area has no script
   to untick it (D32). */
.order:has(.plan-option__input--none:checked) .domain-choice__input--own,
.order:has(.plan-option__input--none:checked) .domain-choice__input--own + .domain-choice__option,
.order:has(.plan-option__input--none:checked) .domain-choice__more--own { display: none; }
@media (min-width: 720px) {
  .order:has(.plan-option__input--none:checked) .domain-choice__options { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.domain-choice__option {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 46px 16px 18px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface-elevated);
  cursor: pointer;
  transition: border-color 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease;
}
.domain-choice__option:hover { border-color: var(--color-border-hover); background: var(--color-bg); }
.domain-choice__title { font-size: 15px; font-weight: 600; color: var(--color-text-primary); }
.domain-choice__desc { font-size: 13px; line-height: 1.4; color: var(--color-text-secondary); }
/* "Use an existing domain" hands the client a task; its full explanation shows
   under the three while that answer is chosen. */
.domain-choice__more { display: none; grid-column: 1 / -1; margin: 2px 0 0; font-size: 13px; color: var(--color-text-muted); }
.domain-choice__input--own:checked ~ .domain-choice__more--own { display: block; }
.order__domain-field { margin: 18px 0 0; }

/* --- The chosen mark, the chosen state, focus and refusal: one set for all
   three pickers, so a plan, a term and a domain answer read as the same kind of
   choice.

   NO CIRCLE ANY MORE (D105). Each card carried a drawn radio — an empty ring on
   every card, a dot in the chosen one. The card is the control: the whole of it
   is the click target, and the chosen one is already drawn as edge.lt's popular
   card below. What is left of the radio is a BADGE ON THE CHOSEN CARD ONLY, top
   right: a small rounded square of the darker lime with a check in it.

   A SQUARE, AND STILL NOT A CHECKBOX. A square reads as "tick as many as you
   like" when it sits EMPTY beside an option, waiting. This one is never empty
   and never beside an unchosen option — it appears on the one card chosen and
   moves when another is — so it says "this one", not "tick me".

   --color-accent-hover, the checked radio's lime: 3.09:1 on the chosen card's
   --color-bg in light mode, 15:1 in dark (WCAG 1.4.11 asks 3:1). The check is
   drawn in --color-bg on it, the same pair the other way round. */
/* THE CHOSEN CARD IS DRAWN AS edge.lt DRAWS ITS POPULAR CARD (D87d) —
   .pricing-card.is-featured, declaration for declaration: the lime border,
   --color-bg, and --shadow-md. edge.lt has no other selected state to borrow
   for a card; the badge above marks the chosen one.
   D87 had drawn a lime wash over the card and a 3px lime halo outside it —
   taken from this panel's older .choice__option and .product--chosen — which
   is a look the main site uses nowhere. A test compares this rule with
   style.css's, so the two cannot drift. */
.plan-option__input:checked + .plan-option__card,
.term-option__input:checked + .term-option,
.domain-choice__input:checked + .domain-choice__option {
  border-color: var(--color-accent);
  background: var(--color-bg);
  box-shadow: var(--shadow-md);
}
/* THE BADGE: ::before is the square, ::after the check drawn over it (a
   positioned ::after paints after ::before). The check is two borders of a
   small box turned 45 degrees — no image, so it takes the token's colour in
   both palettes. A test holds the square's lime to the checked radio's. */
.plan-option__input:checked + .plan-option__card::before,
.term-option__input:checked + .term-option::before,
.domain-choice__input:checked + .domain-choice__option::before {
  content: "";
  position: absolute;
  top: 14px;
  right: 14px;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--color-accent-hover);
}
.plan-option__input:checked + .plan-option__card::after,
.term-option__input:checked + .term-option::after,
.domain-choice__input:checked + .domain-choice__option::after {
  content: "";
  position: absolute;
  top: 18px;
  right: 21px;
  box-sizing: border-box;
  width: 6px;
  height: 11px;
  border: solid var(--color-bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
/* A term card is shorter: the badge sits a little closer to its corner. */
.term-option__input:checked + .term-option::before { top: 10px; right: 10px; }
.term-option__input:checked + .term-option::after { top: 14px; right: 17px; }
/* "HOSTINGO NEREIKIA" CARRIES THE SAME BADGE, AT THE END OF ITS WORDS (D123).
   Four pixels above the words' top, as a card's badge sits four pixels above
   its plan name (14px against 18px) — so on a desktop, where the line sits at
   the plan names' height, its badge is level with theirs. */
.plan-option__input--none:checked + .plan-picker__none .plan-picker__none-text::before {
  content: "";
  position: absolute;
  top: -4px;
  right: 0;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--color-accent-hover);
}
.plan-option__input--none:checked + .plan-picker__none .plan-picker__none-text::after {
  content: "";
  position: absolute;
  top: 0;
  right: 7px;
  box-sizing: border-box;
  width: 6px;
  height: 11px;
  border: solid var(--color-bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
/* IN FORCED COLOURS the badge is drawn in the system's colours on purpose. The
   browser removes author backgrounds there, so the badge — and with it the
   clearest sign of which plan, term or answer is chosen — would vanish, since
   the real radio is hidden. forced-color-adjust: none on the badge alone, with
   system colour keywords only, keeps it in the user's own palette. */
@media (forced-colors: active) {
  .plan-option__input:checked + .plan-option__card::before,
  .term-option__input:checked + .term-option::before,
  .domain-choice__input:checked + .domain-choice__option::before,
  .plan-option__input--none:checked + .plan-picker__none .plan-picker__none-text::before {
    forced-color-adjust: none;
    background: Highlight;
  }
  .plan-option__input:checked + .plan-option__card::after,
  .term-option__input:checked + .term-option::after,
  .domain-choice__input:checked + .domain-choice__option::after,
  .plan-option__input--none:checked + .plan-picker__none .plan-picker__none-text::after {
    forced-color-adjust: none;
    border-color: HighlightText;
  }
  .plan-option__input:checked + .plan-option__card,
  .term-option__input:checked + .term-option,
  .domain-choice__input:checked + .domain-choice__option { border-color: Highlight; }
}
/* THE FOCUSED RADIO IS HIDDEN, SO ITS RING GOES ON THE CARD — the same two
   rings as the global :focus-visible rule, the charcoal band carrying the 3:1
   (WCAG 1.4.11) that lime alone does not reach. */
.plan-option__input:focus-visible + .plan-option__card,
.term-option__input:focus-visible + .term-option,
.domain-choice__input:focus-visible + .domain-choice__option,
.plan-option__input--none:focus-visible + .plan-picker__none .plan-picker__none-text {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--color-text-primary);
}
.plan-option__input[aria-invalid="true"] + .plan-option__card,
.domain-choice__input[aria-invalid="true"] + .domain-choice__option { border-color: var(--tint-red-600); }

/* --- The one button, and what happens next -------------------------------- */
.order__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 20px;
  padding-top: 24px;
  border-top: 1px solid var(--color-border);
}
.order__next { margin: 0; font-size: 14px; color: var(--color-text-muted); }

/* --- Step 2: the order restated, then what the domain answer needs --------
   TWO CARDS IN THE SAME SURFACE as the plan cards, so the review reads as the
   same order one step on rather than a different page. */
.order-summary, .order-step {
  margin: 0 0 20px;
  padding: 22px 24px;
  border: 1px solid var(--color-border);
  border-radius: 0 20px 20px 20px;
  background: var(--color-surface-elevated);
  box-shadow: var(--shadow-sm);
}
.order-summary__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 16px; }
.order-summary__change { font-size: 14px; font-weight: 500; color: var(--color-text-primary); text-underline-offset: 3px; }
.order-summary__list { display: grid; gap: 10px; margin: 4px 0 0; }
.order-summary__row { display: grid; grid-template-columns: minmax(8rem, 12rem) 1fr; gap: 2px 16px; }
.order-summary__row dt { font-size: 14px; color: var(--color-text-muted); }
.order-summary__row dd { margin: 0; font-weight: 500; color: var(--color-text-primary); }
.order-summary__op { display: block; font-size: 14px; font-weight: 400; color: var(--color-text-secondary); }
@media (max-width: 479px) {
  .order-summary__row { grid-template-columns: minmax(0, 1fr); }
}
.order-step .field:last-of-type { margin-bottom: 20px; }
/* ON A PHONE THE THREE STEPS WRAP, and a connector left at the start of the
   second line points at nothing. The numbers already say the order. */
@media (max-width: 479px) {
  .order-progress { gap: 6px 14px; }
  .order-progress__step + .order-progress__step::before { display: none; }
}

/* --- The plans as a table: the row headings once, down the left (D87b) -----
   The four labels were printed inside every card, and the values they belong to
   did not line up from one card to the next. Here the labels are a column of
   their own and the cards are the table's columns.

   SUBGRID IS WHAT MAKES IT A TABLE. Each card spans the picker's six rows —
   name, price and four values — and takes those same rows as its own, so every
   card's "Duomenų bazės" sits on one line with the label on the left, whatever
   any card's text wraps to. Without subgrid (an older browser) none of this
   applies and the cards keep their own labels, which is the layout below.

   THE COLUMN COUNT COMES FROM THE VIEW as a class: a stylesheet cannot count
   the plans, and an inline style is forbidden here (CSP style-src 'self').
   auto-fit cannot do it either — the terms row spans every column, so no track
   is ever empty enough to collapse.

   SIX ROWS = the two heading rows plus SPEC_LABELS, which is four. A test holds
   this number to the label list, because the list lives in PHP. */
.plan-picker__labels { display: none; }

@media (min-width: 1024px) {
  @supports (grid-template-rows: subgrid) {
    .plan-picker__grid--table {
      display: grid;
      grid-template-rows: auto auto repeat(4, auto);
      align-items: stretch;
      column-gap: 12px;
      row-gap: 0;
    }
    .plan-picker__grid--table.plan-picker__grid--cols-2 { grid-template-columns: max-content repeat(2, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-3 { grid-template-columns: max-content repeat(3, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-4 { grid-template-columns: max-content repeat(4, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-5 { grid-template-columns: max-content repeat(5, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-6 { grid-template-columns: max-content repeat(6, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-7 { grid-template-columns: max-content repeat(7, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-8 { grid-template-columns: max-content repeat(8, minmax(0, 1fr)); }
    .plan-picker__grid--table.plan-picker__grid--cols-many { grid-template-columns: max-content repeat(auto-fill, minmax(8rem, 1fr)); }

    /* The labels take the same rows as the cards, and start at the first value row.
       PINNED TO COLUMN 1 (D123), not left to auto-placement: "Hostingo
       nereikia" is placed in that column's first row, and an auto-placed item
       steps round a cell already taken — the labels would have moved one
       column right and every card with them. */
    .plan-picker__grid--table .plan-picker__labels {
      display: grid;
      grid-column: 1;
      grid-row: 1 / span 6;
      grid-template-rows: subgrid;
      padding-right: 4px;
    }
    /* "HOSTINGO NEREIKIA" IN THE LABEL COLUMN'S EMPTY FIRST ROW (D123), with
       the plan names' 18px above it so the words — and the badge at their end —
       are level with the names and the badges on the cards. Above the label
       column in the stacking order, which is transparent there anyway, so the
       whole line takes the click. The column is max-content, so it widens to
       keep the words on one line and the cards give up that much width. */
    .plan-picker__grid--table .plan-picker__none {
      grid-column: 1;
      grid-row: 1;
      align-self: start;
      z-index: 1;
      padding: 18px 4px 0 0;
    }
    /* THE SAME RHYTHM AS THE VALUES BESIDE IT, with no padding of its own
       beyond the 2px the panel's specification rows keep (D87c). Both sit at
       the TOP of their row, so a value that wraps to a second line grows the
       row downwards and the label stays on its first line. */
    .plan-picker__grid--table .plan-picker__label {
      padding: 1px 0;
      font-size: 14px;
      line-height: 1.6;
      color: var(--color-text-muted);
    }
    /* EACH LABEL IS PLACED, NOT FLOWED. Placing only the first sent the other
       three back to the top of the column: auto-placement starts its cursor at
       row 1 regardless of where an explicitly placed item sits, so the labels
       landed on the plan names and in the wrong order. One row apiece, and the
       count matches SPEC_LABELS — a test holds the two together. */
    .plan-picker__grid--table .plan-picker__label:nth-child(1) { grid-row: 3; }
    .plan-picker__grid--table .plan-picker__label:nth-child(2) { grid-row: 4; }
    .plan-picker__grid--table .plan-picker__label:nth-child(3) { grid-row: 5; }
    .plan-picker__grid--table .plan-picker__label:nth-child(4) { grid-row: 6; }

    /* THE CARD'S VERTICAL PADDING MOVES ONTO ITS ROWS. A subgrid takes its own
       padding out of the first and last track it spans, which would drop the
       last value below its label by the height of the padding. */
    .plan-picker__grid--table .plan-option__card {
      display: grid;
      grid-row: 1 / span 6;
      grid-template-rows: subgrid;
      gap: 0;
      padding: 0 16px;
      min-width: 0;
    }
    .plan-picker__grid--table .plan-option__name { padding: 18px 26px 0 0; }
    .plan-picker__grid--table .plan-option__price { padding: 6px 0 10px; }
    /* The four values become rows of the card's subgrid, so they line up with
       the labels; each card keeps its own labels for a screen reader. */
    .plan-picker__grid--table .plan-option__spec,
    .plan-picker__grid--table .plan-option__spec-row { display: contents; }
    .plan-picker__grid--table .plan-option__spec-label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
    }
    .plan-picker__grid--table .plan-option__spec-value { padding: 1px 0; }
    /* The card's own bottom padding, carried by its last row (a subgrid gives
       its padding to its last track, which would lift that value off its label). */
    .plan-picker__grid--table .plan-option__spec-row:last-child .plan-option__spec-value { padding-bottom: 16px; }
  }
}
