/* Security BSides Dehradun 0x03
   base.css: reset, element defaults, typographic and layout primitives.

   Everything here is unopinionated about COMPONENTS and opinionated about
   TYPE and SPACE. Component styling lives in components.css.

   A note on the heading strategy, because it is unusual and deliberate:
   h1–h6 are reset to inherit their size. Visual size comes from the .display
   and .h1–.h5 CLASSES instead. That keeps the heading LEVEL free to describe
   document structure for screen readers and search engines while the visual
   size is chosen for the layout, so a section can be an <h2> that looks
   enormous, or an <h2> that looks small, without ever skipping a level. */

/* One rule in this project sits outside @layer, and this is it.

   @view-transition is a document-level opt-in, not a style rule: it has no
   selector and no declarations that cascade, so there is nothing for a layer
   to order it against. It parses inside @layer in Chrome. That was checked
   rather than assumed, but the specification defines it as a top-level rule,
   and a rule that only works because one engine is lenient is not a rule to
   build a site's navigation on.

   `navigation: auto` is what turns every same-origin link on this site into a
   cross-document view transition: the header and footer stay put while the
   content cross-fades. Both documents must opt in, which is why this lives in
   base.css: the one stylesheet all fourteen pages load, 404 included.

   Browsers without support ignore the whole at-rule and navigate normally.
   There is nothing to feature-detect and nothing to fall back to.
   See docs/DESIGN-BRIEF.md §5, which sanctions exactly this. */
@view-transition {
  navigation: auto;
}

/* Reduced motion switches the opt-in off at source, so a cross-document
   transition never begins. `navigation: none` is the initial value; naming it
   here is the point. The pseudo-element neutralisation further down catches
   the script-initiated case, which the navigation switch cannot reach. */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

@layer base {

  /* reset */

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

  html {
    /* Anchor targets must clear the sticky header. */
    scroll-padding-block-start: 6rem;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    tab-size: 4;
  }

  /* Smooth scrolling is opt-in via JS so it can be disabled for
     prefers-reduced-motion without fighting a CSS declaration. */

  body {
    font-family: var(--font-sans);
    font-size: var(--fs-base);
    font-weight: var(--fw-regular);
    line-height: var(--lh-body);
    letter-spacing: var(--track-body);
    color: var(--ink);
    background-color: var(--paper);

    /* Deliberately NO overflow-x here. Both available values break something:
         · `hidden` turns body into a scroll container, which silently kills
           `position: sticky` on the header.
         · `clip` keeps sticky working but establishes a containing block that
           CLIPS fixed-position descendants, which traps the mobile nav
           overlay inside the header instead of letting it cover the viewport.
       So the safety net goes on `main` instead (below), where page content
       can overflow but nothing is fixed. Sideways scroll is prevented at the
       source: every grid track is minmax(0, 1fr), never a bare 1fr. */

    min-height: 100svh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-synthesis: none;
    font-kerning: normal;
    font-variant-ligatures: common-ligatures contextual;
  }

  /* The overflow safety net. `clip` rather than `hidden` so sticky elements
     inside page content keep working. Do not put a position: fixed element
     inside <main>. It will be clipped here. Fixed things belong to the
     header, the footer or body level. */
  main {
    overflow-x: clip;
  }

  /* type scale as classes */

  h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    letter-spacing: inherit;
  }

  .display,
  .h1, .h2, .h3, .h4, .h5 {
    font-family: var(--font-sans);
    color: var(--ink);
    text-wrap: balance;
    hyphens: none;
  }

  .display {
    font-size: var(--fs-display);
    line-height: var(--lh-display);
    letter-spacing: var(--track-display);
    font-weight: var(--fw-medium);
  }

  .h1 {
    font-size: var(--fs-4xl);
    line-height: var(--lh-heading);
    letter-spacing: var(--track-display);
    font-weight: var(--fw-medium);
  }

  .h2 {
    font-size: var(--fs-3xl);
    line-height: var(--lh-heading);
    letter-spacing: var(--track-3xl);
    font-weight: var(--fw-medium);
  }

  .h3 {
    font-size: var(--fs-2xl);
    line-height: 1.1;
    letter-spacing: var(--track-2xl);
    font-weight: var(--fw-medium);
  }

  .h4 {
    font-size: var(--fs-xl);
    line-height: var(--lh-snug);
    letter-spacing: var(--track-xl);
    font-weight: var(--fw-medium);
  }

  .h5 {
    font-size: var(--fs-lg);
    line-height: var(--lh-snug);
    letter-spacing: var(--track-body);
    font-weight: var(--fw-medium);
  }

  /* text elements */

  p {
    text-wrap: pretty;
    max-width: var(--measure);
  }

  /* A paragraph inside a grid cell or component should not be re-constrained
     by --measure; the container is already doing that job. */
  .grid > p,
  [class*="col-"] > p {
    max-width: none;
  }

  strong, b { font-weight: var(--fw-bold); }
  em, i     { font-style: italic; }

  small {
    font-size: var(--fs-sm);
    line-height: var(--lh-snug);
  }

  a {
    color: inherit;
    text-decoration: none;

    /* Underline drawn as a background gradient rather than text-decoration.
       This animates smoothly, sits at a controllable distance from the
       baseline, and (unlike a border) wraps correctly across lines. */
    background-image: linear-gradient(currentColor, currentColor);
    background-repeat: no-repeat;
    background-position: 0 100%;
    background-size: 100% 1px;
    padding-block-end: 0.08em;
    transition:
      background-size var(--dur) var(--ease),
      color var(--dur-fast) var(--ease);
  }

  /* THE POINTER GUARD, and it wraps every :hover rule in this project from
     2 September 2026 onward. Read this once and the other thirty read as
     bookkeeping.

     A touchscreen has no cursor to take away. When a finger taps a link, the
     browser applies :hover to satisfy sites that only style hover, and then
     leaves it applied until something else is touched. So on a phone, before
     this guard, tapping any nav item left it lit in the accent colour
     afterwards, which is exactly how the current page is marked one rule
     further down. A stuck hover was not a cosmetic blemish there, it was the
     site telling the reader they were somewhere they were not.

     `(hover: hover) and (pointer: fine)` rather than `(hover: hover)` alone,
     because the pair is already what this project uses: main.js line 272 gates
     the nav's hover-intent opening on precisely that query. The stylesheet
     simply had not caught up with the script, and a codebase where the JS
     believes in pointer detection and the CSS does not is one bug either way.

     The touch path is not left with nothing. :active now carries the feedback
     it should always have carried, and it fires on touch and pointer alike.

     One caution for anyone extending this. Where a rule listed :hover and
     :focus-visible together, the two were SPLIT rather than wrapped as a pair.
     Wrapping both would have deleted the focus ring on any device reporting
     `hover: none`, and a tablet with a keyboard is exactly that device. Hover
     is a pointer affordance and may be gated; focus never may. */
  @media (hover: hover) and (pointer: fine) {
    a:hover {
      color: var(--accent);
    }
  }

  a:active {
    color: var(--accent-active);
  }

  /* Links that are structural rather than editorial opt out of the underline
     by carrying a component class; those are handled in components.css. */

  ul, ol {
    padding-inline-start: 1.25em;
  }

  li + li {
    margin-block-start: var(--s-2);
  }

  li::marker {
    color: var(--ink-3);
  }

  dl { display: grid; }
  dt { font-weight: var(--fw-medium); }
  dd { color: var(--ink-2); }

  blockquote {
    margin: 0;
  }

  figure { margin: 0; }

  figcaption {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: var(--track-mono);
    color: var(--ink-3);
    margin-block-start: var(--s-3);
  }

  hr {
    border: 0;
    border-block-start: var(--bw) solid var(--rule);
    margin-block: var(--s-6);
  }

  address {
    font-style: normal;
  }

  /* Mono is for metadata. Applying it at the element level keeps page
     authors from having to remember a utility class for these. */
  code, pre, kbd, samp {
    font-family: var(--font-mono);
    font-size: 0.925em;
    letter-spacing: var(--track-mono);
  }

  code {
    background: var(--ink-wash);
    padding: 0.15em 0.4em;
    border-radius: var(--r-1);
  }

  pre {
    overflow-x: auto;
    padding: var(--s-4);
    border: var(--bw) solid var(--rule);
    background: var(--paper-sunk);
  }

  pre code {
    background: none;
    padding: 0;
  }

  kbd {
    border: var(--bw) solid var(--rule-strong);
    border-radius: var(--r-1);
    padding: 0.1em 0.35em;
    font-size: 0.85em;
  }

  mark {
    background: var(--accent-tint);
    color: var(--ink);
    padding: 0 0.15em;
  }

  abbr[title] {
    text-decoration: underline dotted;
    text-underline-offset: 0.2em;
    cursor: help;
  }

  time {
    font-variant-numeric: tabular-nums;
  }

  table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
  }

  th, td {
    text-align: start;
    padding: var(--s-3) var(--s-4);
    border-block-end: var(--bw) solid var(--rule);
    vertical-align: baseline;
  }

  th {
    font-family: var(--font-mono);
    font-size: var(--fs-label);
    font-weight: var(--fw-regular);
    text-transform: uppercase;
    letter-spacing: var(--track-label);
    color: var(--ink-3);
  }

  /* replaced elements */

  img, svg, video, canvas, picture {
    display: block;
    max-width: 100%;
  }

  img, video {
    height: auto;
  }

  /* controls */

  button, input, select, textarea {
    font: inherit;
    letter-spacing: inherit;
    color: inherit;
    background: none;
    border: 0;
    border-radius: 0;
    appearance: none;
  }

  button {
    cursor: pointer;
    text-align: inherit;
  }

  button:disabled,
  [aria-disabled="true"] {
    cursor: not-allowed;
  }

  ::placeholder {
    color: var(--ink-3);
    opacity: 1;
  }

  textarea {
    resize: vertical;
    min-height: 8lh;
  }

  /* Native disclosure widgets are used for the FAQ so it works without JS. */
  summary {
    cursor: pointer;
    list-style: none;
  }

  summary::-webkit-details-marker {
    display: none;
  }

  /* selection, focus */

  ::selection {
    background: var(--selection-bg);
    color: var(--ink);
  }

  :focus-visible {
    outline: var(--bw-strong) solid var(--accent);
    outline-offset: 3px;
    border-radius: var(--r-1);
  }

  /* Suppress the ring for pointer interactions only, never remove it for
     keyboard users. */
  :focus:not(:focus-visible) {
    outline: none;
  }

  /* layout: shell */

  .shell {
    width: min(100% - (var(--page-margin) * 2), var(--container));
    margin-inline: auto;
  }

  .shell--narrow {
    width: min(100% - (var(--page-margin) * 2), var(--container-narrow));
    margin-inline: auto;
  }

  /* layout: grid */

  .grid {
    display: grid;
    grid-template-columns: repeat(var(--cols, 12), minmax(0, 1fr));
    gap: var(--row-gap, var(--gutter)) var(--gutter);
  }

  .col-1  { grid-column: auto / span 1; }
  .col-2  { grid-column: auto / span 2; }
  .col-3  { grid-column: auto / span 3; }
  .col-4  { grid-column: auto / span 4; }
  .col-5  { grid-column: auto / span 5; }
  .col-6  { grid-column: auto / span 6; }
  .col-7  { grid-column: auto / span 7; }
  .col-8  { grid-column: auto / span 8; }
  .col-9  { grid-column: auto / span 9; }
  .col-10 { grid-column: auto / span 10; }
  .col-11 { grid-column: auto / span 11; }
  .col-12 { grid-column: auto / span 12; }

  .col-start-1  { grid-column-start: 1; }
  .col-start-2  { grid-column-start: 2; }
  .col-start-3  { grid-column-start: 3; }
  .col-start-4  { grid-column-start: 4; }
  .col-start-5  { grid-column-start: 5; }
  .col-start-6  { grid-column-start: 6; }
  .col-start-7  { grid-column-start: 7; }
  .col-start-8  { grid-column-start: 8; }
  .col-start-9  { grid-column-start: 9; }
  .col-start-10 { grid-column-start: 10; }
  .col-start-11 { grid-column-start: 11; }

  /* Tablet: collapse to six columns. Any cell wider than six columns, and
     any explicit start position, is released to full width, which is almost
     always the right answer. Cells that should stay narrow opt in with
     .col-sm-N. */
  @media (max-width: 60rem) {
    .grid { --cols: 6; }

    .grid > [class*="col-"]       { grid-column: auto / span 6; }
    .grid > [class*="col-start-"] { grid-column-start: auto; }

    .grid > .col-sm-1 { grid-column: auto / span 1; }
    .grid > .col-sm-2 { grid-column: auto / span 2; }
    .grid > .col-sm-3 { grid-column: auto / span 3; }
    .grid > .col-sm-4 { grid-column: auto / span 4; }
  }

  /* Phone: single column, always. */
  @media (max-width: 40rem) {
    .grid { --cols: 1; }
    .grid > [class*="col-"] { grid-column: auto / span 1; }
  }

  /* layout: stack */

  /* Flow spacing applied by the parent, so children never carry margins.
     The lobotomised-owl selector: space BETWEEN siblings only. */
  .stack > * + * { margin-block-start: var(--stack); }

  .stack--xs > * + * { margin-block-start: var(--s-2); }
  .stack--sm > * + * { margin-block-start: var(--s-3); }
  .stack--md > * + * { margin-block-start: var(--s-5); }
  .stack--lg > * + * { margin-block-start: var(--s-7); }
  .stack--xl > * + * { margin-block-start: var(--s-8); }

  /* long-form prose */

  /* Used on the Code of Conduct page and anywhere a genuine reading
     experience matters. Serif, generous leading, narrow measure. */
  .prose {
    font-family: var(--font-serif);
    font-size: var(--fs-lg);
    line-height: var(--lh-prose);
    letter-spacing: 0;
    color: var(--ink-2);
    max-width: var(--measure-lead);
    text-wrap: pretty;
  }

  .prose > * + * { margin-block-start: 1.1em; }

  .prose :is(h2, h3, h4) {
    font-family: var(--font-sans);
    color: var(--ink);
    text-wrap: balance;
    letter-spacing: var(--track-2xl);
  }

  .prose h2 {
    font-size: var(--fs-2xl);
    line-height: 1.1;
    margin-block-start: 2.2em;
  }

  .prose h3 {
    font-size: var(--fs-xl);
    line-height: var(--lh-snug);
    letter-spacing: var(--track-xl);
    margin-block-start: 1.8em;
  }

  .prose h4 {
    font-size: var(--fs-lg);
    letter-spacing: var(--track-body);
    margin-block-start: 1.6em;
  }

  .prose :is(h2, h3, h4) + * { margin-block-start: 0.6em; }

  .prose p { max-width: none; }

  .prose :is(ul, ol) { padding-inline-start: 1.4em; }
  .prose li + li     { margin-block-start: 0.4em; }

  .prose a {
    color: var(--accent);
    background-size: 100% 1px;
  }

  @media (hover: hover) and (pointer: fine) {
    .prose a:hover {
      color: var(--accent-hover);
    }
  }

  .prose a:active {
    color: var(--accent-active);
  }

  .prose strong { color: var(--ink); }

  .prose blockquote {
    padding-inline-start: var(--s-5);
    border-inline-start: var(--bw-strong) solid var(--accent);
    color: var(--ink);
  }

  /* Small mono asides inside prose, used for reporting contacts, notes. */
  .prose .note {
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    line-height: var(--lh-snug);
    letter-spacing: var(--track-mono);
    color: var(--ink-3);
    border-inline-start: var(--bw) solid var(--rule);
    padding-inline-start: var(--s-4);
  }

  /* lead */

  /* The standfirst paragraph under a heading. Serif, large, quiet. This is
     what makes the pages read as editorial rather than as marketing. */
  .lead {
    font-family: var(--font-serif);
    font-size: var(--fs-xl);
    line-height: var(--lh-snug);
    letter-spacing: 0;
    color: var(--ink-2);
    max-width: var(--measure-lead);
    text-wrap: pretty;
  }

  .lead--sm {
    font-size: var(--fs-lg);
  }

  /* motion: the backstop */

  /* Declared last in @layer base and deliberately heavy-handed. Any later
     file that forgets to honour the preference is neutralised here, so brief
     §5 compliance is structural rather than a matter of remembering.

     `!important` inside @layer base is what makes that true. For important
     declarations the cascade reverses layer order (an earlier layer wins),
     so nothing in components, sections or utilities can out-shout this block
     however it is written. That is not a happy accident; it is the reason the
     backstop lives in base rather than in utilities. */
  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 1ms !important;
      animation-delay: 0ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 1ms !important;
      transition-delay: 0ms !important;
      scroll-behavior: auto !important;

      /* The first of the two holes this block used to have. A scroll-driven
         animation derives its progress from a scroll position, not from a
         clock: `animation-duration: 1ms` is simply not a property it reads, so
         every rule above leaves it running at full travel. Detaching the
         timeline is what stops it. Each such animation also has its travel
         tokens zeroed in tokens.css, so this is the second of two independent
         guards rather than the only one. */
      animation-timeline: none !important;
    }

    html { scroll-behavior: auto !important; }

    /* The second hole. The ::view-transition tree is generated *outside* the
       document tree, so the universal selector above never reaches it and the
       UA cross-fade survives everything declared so far. Two separate stops:
       the navigation opt-in below is switched off so a cross-document
       transition never starts, and any transition that is started from script
       (the theme wipe is the only one) has its animations removed here.

       main.js also checks the preference before calling startViewTransition.
       Three guards for one preference is not excessive: this is the clause
       the brief writes as mandatory, and the failure mode is motion sickness. */
    ::view-transition-group(*),
    ::view-transition-image-pair(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
      animation: none !important;
    }
  }

  /* interpolating to a keyword */

  /* `height: auto` is not a number, so historically it could not be animated
     to or from, which is the single reason every accordion on the web is
     either instant or implemented in JavaScript with a measured pixel height.
     `interpolate-size: allow-keywords` lifts that restriction for the whole
     subtree, and the <details> animation in components.css depends on it.

     It is set on :root because it inherits, and it is behind @supports so that
     a browser without it simply keeps the instant open/close, which is a
     working accordion, not a broken one. Nothing else in this codebase
     transitions to or from an intrinsic size, so widening the permission to
     the document has no other effect to reason about. */
  @supports (interpolate-size: allow-keywords) {
    :root {
      interpolate-size: allow-keywords;
    }
  }

  /* the motion policy */
  /* One place, and the only place, that says which pages get the choreography
     and which stay still. Everything the motion layer adds is expressed as
     travel: an entrance is a rise of --lift, a drift is --label-drift. Setting
     those to zero here leaves every rule in place and running, moving nothing.

     The five pages listed are the reading pages: the code of conduct, the
     terms, the privacy notice, and the two pages a submitted form lands on.
     They keep the reading-progress hairline and the cross-page morph, because
     both help you read, and they lose every entrance, because entrance motion
     on a legal page reads as a slow page rather than as polish. The code of
     conduct is the page somebody opens when they are having a bad time at a
     conference; it should be legible the instant it paints.

     A confirmation page belongs in this list for a different reason than a
     legal one: the reader has just pressed a button and is looking for the one
     sentence that says it worked. Making that sentence rise into place delays
     the only thing they came for. `.page-ctf-thanks` was added when the CTF
     form got its own landing page. It was missing for exactly as long as it
     took to notice, which is the argument for the reminder below.

     Adding a reading page? Add its body class here. Nothing else.

     --wipe joins the list because the contour divider is the first piece of the
     motion layer that a reading page has any reason to carry. It is a section
     break rather than an entrance, so it belongs on a long page of clauses more
     than most effects do, and it still has to arrive already drawn there: the
     reader is looking for a clause, not for a band uncovering itself. Zero travel
     leaves the mask painted from the first frame. --ridge-drift is deliberately
     absent, because the band it drives exists on the home hero and nowhere
     else. */
  body:is(.page-coc, .page-terms, .page-privacy, .page-thanks, .page-ctf-thanks) {
    --lift:        0px;
    --lift-line:   0%;
    --label-drift: 0px;
    --wipe:        0%;
    --dur-entrance: 1ms;
    --stagger:      0ms;

    --entrance:      none;
    --entrance-line: none;
  }
}


@layer utilities {

  /* `hidden` is a contract between main.js and the markup: the CFP page ships
     all three status blocks visible and JS hides the two that do not apply. But
     the UA stylesheet's `[hidden] { display: none }` loses to ANY author rule
     that sets `display`, so the moment a hidden element also carries
     `.page-hero__actions { display: flex }`, it stays on screen and the page
     shows three contradictory statuses at once.

     Declared here because @layer utilities is the last layer, which already
     beats every component and section rule. The !important is belt-and-braces
     against unlayered CSS ever being added. */
  [hidden] { display: none !important; }

  /* The clip-path implementation: removes the element visually while leaving
     it in the accessibility tree, and, unlike `display: none` or
     `visibility: hidden`, keeps it focusable. */
  .u-visually-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip-path: inset(50%) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }

  /* Hidden until focused, for skip links and similar. */
  .u-visually-hidden-focusable:not(:focus):not(:focus-within) {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip-path: inset(50%) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }

  .u-no-wrap  { white-space: nowrap; }
  .u-balance  { text-wrap: balance; }
  .u-pretty   { text-wrap: pretty; }

  /* Hexadecimal notation must survive the mono/uppercase treatment that the
     eyebrows, labels and chips apply. `0x03` uppercased becomes `0X03`, which
     is not a stylistic variant. It is wrong. This lives in @layer utilities
     precisely so it outranks every component and section rule without
     needing !important or a specificity hack. Put it on any element whose
     text contains 0x-prefixed hex. */
  .u-hex { text-transform: none; }

  /* Any number that changes must not make the layout jitter. */
  .u-tabular  { font-variant-numeric: tabular-nums; }

  .u-mono {
    font-family: var(--font-mono);
    letter-spacing: var(--track-mono);
  }

  .u-serif { font-family: var(--font-serif); letter-spacing: 0; }

  .u-ink-2 { color: var(--ink-2); }
  .u-ink-3 { color: var(--ink-3); }

  .u-measure        { max-width: var(--measure); }
  .u-measure-narrow { max-width: var(--measure-narrow); }

  .u-mt-0 { margin-block-start: 0 !important; }
  .u-mb-0 { margin-block-end: 0 !important; }

  .u-hide-sm { @media (max-width: 60rem) { display: none !important; } }
  .u-hide-lg { @media (min-width: 60.0625rem) { display: none !important; } }
}
